Preventing router from navigating - sapui5

I need to prevent router to be navigated to another page (which is done by changing the hash) if some changes are made. Tried with HashChanger but it just fires 'hashChange' events with no way to prevent it from bubbling. The answer can be inside of JS-Signals library but it's unavailable directly for user-created SAP components.

There is a stop function on the router https://sapui5.hana.ondemand.com/#/api/sap.ui.core.routing.Router/methods/stop
if you call it, the router will stop listening to hashchanges.
There is also function isStopped().
To (re-)activate the router, call initialize(...).

Instead of stopping the router entirely, navigation can be prevented by event.preventDefault() within the navigate event handler.
<App xmlns="sap.m" navigate=".onNavigate">
onNavigate: function(event) {
if (/* Pending changes, no authorization [1], etc. */) {
event.preventDefault();
const { isBack, isBackToPage, isBackToTop } = event.getParameters();
const isBackNavigation = isBack || isBackToPage || isBackToTop;
window.history.go(isBackNavigation ? 1 : -1);
// Inform the user ...
}
},
Demo: https://embed.plnkr.co/wp6yes
Further discussion about interrupting navigation: https://github.com/SAP/openui5/issues/3411
[1]: If the reason for preventing navigation is the lack of authorization, it's not enough to block the user on the client-side only. The server needs to make sure that no unauthorized resource is sent to the client in the first place. See the response from #matz3 on GitHub.

Related

How to do actions when MongoDB Realm Web SDK change stream closes or times out?

I want to delete all of a user's inserts in a collection when they stop watching a change stream from a React client. I'm using the Realm Web SDK for this.
Here's a summary of my code with what I want to do at the end of it:
import * as Realm from "realm-web";
const realmApp: Realm.App = new Realm.App({ id: realmAppId });
const credentials = Realm.Credentials.anonymous();
const user: Realm.User = await realmApp.logIn(credentials);
const mongodb = realmApp?.currentUser?.mongoClient("mongodb-atlas");
const users = mongodb?.db("users").collection("users");
const changeStream = users.watch();
for await (const change of changeStream) {
switch (change.operationType) {
case "insert": {
...
break;
}
case ...
}
}
// This pseudo-code shows what I want to do
changeStream.on("close", () => // delete all user's inserts)
changeStream.on("timeout", () => // delete all user's inserts)
changeStream.on("user closes app thus also closing stream", () => ... )
Realm Web SDK patterns seem rather different from the NodeJS ones and do not seem to include a method for closing a stream or for running a callback when it closes. In any case, they don't fit my use case.
These MongoDB Realm Web docs lead to more docs about Realm. Unless I'm missing it, both sets don't talk about how to monitor for closing and timing out of a change stream watcher instantiated from the Realm Web SDK, and how to do something when it happens.
I thought another way to do this would be in Realm's Triggers. But it doesn't seem likely from their docs.
Can this even be done from a front end client? Is there a way to do this on MongoDB itself in a "serverless" way?
If you want to delete the inserts specifically when a (client-)listener of a change-stream stops listening you have to implement some logic on client side. There is currently no way to get notified of such even within Mongodb Realm.
Sice a watcher could be closed because the app / browser is closed I would recommend against running the deletion logic on your client. Instead notify a server (or call a Mongodb Realm function / http endpoint) to make the deletions.
You can use the Beacon API to reliably send a request to trigger the delete, even when the window unloads.
Client side
const inserts = [];
for await (const change of changeStream) {
switch (change.operationType) {
case 'insert': inserts.push(change);
}
}
// This point is only reached if the generator returns / stream closes
navigator.sendBeacon('url/to/endpoint', JSON.stringify(inserts));
// Might also add a handler to catch users closing the app.
window.addEventListener('unload', sendBeacon);
Note that the unload event is not reliable MDN. But there are some alternatives which maybe be good enough for your use case.
Inside a realm function you could delete the documents.
That being said, maybe there is a better way to do what you want to achieve. Is it really the timeout of the change stream listener that has to trigger the delete or some other userevent?

Is it possible to execute a long running function before the browser is reloaded?

I do prevent a page reload in my web application by the following function:
window.onbeforeunload = (event) => {
const e = event || window.event;
// Cancel the event
e.preventDefault();
save_user_data_to_indexed_db();
if (e) {
e.returnValue = ''; // Legacy method for cross browser support
}
return ''; // Legacy method for cross browser support
};
However, the save_user_data_to_indexed_db() function is not being executed during the "Reload site?" message. I thought that if I could execute my function during the displayed message, I could maybe automatically answer the same dialog programmatically and let the browser continue reloading the page.
Is there a way to make the browser wait for this kind of operation?
Generally, there is no way to make the browser wait. What I often do in this case is write the data to an intermediate place, such as localStorage, synchronously, and then asynchronously copy that data over to indexedDB later on when there is time, such as when the page is next loaded again, or from within a service worker.

self.addEventListener('fetch', function(e) { }) is not working

I have a doubt in PWA and will be glad if someone helps me with that. In my PWA I don't have any problem with storing static files like HTML, JS & CSS. But am facing Issues on dynamic data. i.e : my self.addEventListener('fetch', function(e) { }) is not getting called, but other functionalities are working fine i.e: 'install' and 'active' event.
to be more particular, I am using #angular/service-worker which worked fine but I created another sw file called sw.js. In my sw-js I'm listening to the events like 'install' 'active' and 'fetch'. My sw.js fetch is not getting called whereas the other two methods work well. But while fetching the ngsw-worker.js's fetch method alone gets called.
The thing I need is to make CRUD Operations in PWA with angular.
Thanks in advance!
You can do the dynamic caching like below , the service worker will intercept every request and add in to the cache.
self.addEventListener("fetch", function (event) {
event.respondWith(
caches.open("dynamiccache").then(function (cache) {
return fetch(event.request).then(function (res) {
cache.put(event.request, res.clone());
return res;
})
})
)
}
Note : You can't cache POST Requests
Can service workers cache POST requests?

What is the EventBus in SAPUI5 for?

Can anybody explain for what and when we are going to use EventBus methods? Also what kind the activities of the same.
EventBus in UI5 is a tool with which we can leverage publish-subscribe pattern in our app.
How do we get EventBus?
Currently, there are two APIs which return their own instance of EventBus:
Globally: sap.ui.getCore().getEventBus(); for
Standalone apps.
Component apps and their container app where developers have control over both.
Component-based: this.getOwnerComponent().getEventBus(); // this == controller. Especially for apps targeting Fiori Launchpad (FLP) where SAP explicitly warns not to get the EventBus from the core but from the component:
If you need an event bus, use the event bus of the component. By this, you avoid conflicting event names and make sure that your listeners are automatically removed when the component is unloaded. Do not use the global event bus.
Note
FLP destroys the component every time when the user navigates back Home.
Make sure to have the module sap/ui/core/EventBus required before calling getEventBus() to properly declare the dependency and to avoid possible sync XHR when requiring it.
sap.ui.define([ // or sap.ui.require
// ...,
"sap/ui/core/EventBus",
], function(/*...,*/ EventBus) { /*...*/ });
What is it for?
With EventBus, we can fire (via publish()), and listen (via subscribe()) to our own custom events freely:
Without having to use or extend any Control / ManagedObject / EventProvider classes,
Without knowing the existence of other involved listeners (if any),
Without accessing the object that fires the event (publisher). E.g.: No need to call thatManagedObj.attach*().
Publishers and subscribers stay ignorant to each other which makes loose coupling possible.
Analogous to the real world, EventBus is like a radio station. Once it starts to broadcast about all sorts of things on various channels, those, who are interested, can listen to a particular channel, get notified about a certain event, and do something productive with the given data. Here is an image that illustrates the basic behavior of an EventBus:
Sample code
Subscribe
{ // Controller A
onInit: function() {
const bus = this.getOwnerComponent().getEventBus();
bus.subscribe("channelABC", "awesomeEvent", this.shouldDoSomething, this);
},
shouldDoSomething: function(channelId, eventId, parametersMap) {
// Get notified when e.g. "doSomething" from Controller B is called.
},
}
Publish
{ // Controller B
doSomething: function(myData) {
const bus = this.getOwnerComponent().getEventBus();
bus.publish("channelABC", "awesomeEvent", { myData }); // broadcast the event
},
}
See API reference: sap/ui/core/EventBus

Sails.js socket.io general security

Using sails sockets.
From a browser I can get all 'tasks' where the user id is 1.
I can now listen for the 'task' event and look for 'created' in the verb to get new tasks and add them to the list.
However I get events from ALL created tasks regardless of user. This seems to be me as a major security issue. All someone needs to do jump into the console and set up a listener to get notified whenever any user creates a new task.
I had a look around for sometime but can't find any posts on the topic.
Being new to this kind of thing - can someone be kind enough to help out?
What is the best practise for dealing with lists over socket.io in Sails?
Cheers!
This should be what you're looking for; it prevents you from subscribing to all existing tasks on the client side. It only subscribes if you're logged in and only to tasks that belong to you. Keep in mind that this is just a first-step in implementing a secure REST API for your app - but it should get you started.
In your client-side app you'd write:
socket.on('connect', function socketConnected()
{
// This subscribes the user to all tasks that belong to him and only him.
socket.get('/task/subscribe', null, function response(data, jwres)
{
// We don’t really care about the response.
});
// This 1.) creates a new task and 2.) subscribes the user to that task.
// If the 'rest' blueprint is on, POSTing to /task gets redirected to TaskController.create automatically by sails.
// If it's not on, you write "socket.get('/task/create' ..."
socket.post('/task', {name : 'MyNewTask'}, function response(data, jwres)
{
// Add the created task inside of 'data' to your client side app.
});
})
Then in TaskController.js you would write:
subscribe : function(req, res)
{
// Is the user logged in?
if(!req.session.user)
{
return res.badRequest();
}
// Find all tasks that belong to the currently logged in user.
Task.find({userID : req.session.user.id}, findUsersCB(err, tasks)
{
// Subscribe the user to all of his tasks.
Task.subscribe(req.socket, tasks);
// Send user's tasks back to the client.
res.json(tasks);
});
}
create : function(req, res)
{
//Is the user logged in?
if(!req.session.user)
{
return res.badRequest();
}
var taskToBeCreated =
{
name : req.param('name'),
userID : req.session.user.id;
};
// Attempt to create the given task.
Task.create(taskToBeCreated, function createTaskCB(err, createdTask)
{
// Subscribe the user to the newly-created task.
Task.subscribe(req.socket, createdTask);
// Send user's task back to the client.
res.json(task);
});
}
I haven't shown an example for the 'update' and 'destroy' actions but the idea is the same for both.