In my application, I'm using an OnDemandGrid, backed by a Request + Trackable dstore, to display my data to the user.
The server sends notifications to the client via websocket to add new entries to the grid. To add the new entries to the grid, the store is emiting an 'add' event, something like the following piece of code:
function _emitAddEvent(store, entity) {
store.emit('add', {
target: entity,
id: entity.id
});
}
Until here, it's all good. The application receives the new entry from the server to be added to the grid, and adds it (without refreshing the grid). The problem is if there are too many notifications being sent by the server during a small time interval. The store emits all the events to the dgrid, but the grid tooks some time to render all the rows. Because there are too many entries to be added, the application goes unresponsive. If the server stops sending data to the client, after some time the application recovers and render all the rows correctly. Now comes the second (but minor) issue.
The second issue is that, after the grid renders all the new rows, it does not destroy the rows that are too far. I've set the farOffRemoval attribute, but it seems to only handle cases where a scroll happens and new data is requested to the server. I would like to know if there is a workaround, that does not rely on scroll, to destroy nodes that are too distant from the user current position on the grid.
Well, I've managed to code a workaround to the first issue. Since the application was becoming unresponsive because there were a lot of notifications being received by the client in short period of time, I opted for adding the events on a queue and emit them at a maximum of one per second. dojo/throttle along with setInterval were enough for that.
The second isue, related to the farOffRemoval attribute, I wasn't able to solve. After some testing, I noticed that the browser can have a lot of DOM nodes without losing significant performance (of course, it depends on the user machine), so I just left the grid untouched.
Related
When we perform destructive operations on database tables (insert/update/delete) using ajax, the DOM has to reflect this change. I see two commonly used approaches.
When the destructive database operation succeeds, manipulate the rows in the html table to reflect the change
When the destructive database operation succeeds, refresh the html table by making another ajax request
Suppose users A and B are accessing a table of 5 rows. Then A deletes the first row while B deletes the second row. With the DOM manipulation approach, A will not see that the second row is deleted while B will not see that the first row is deleted. This problem can be eliminated by the refresh approach, but both users can only see the full updates when they themselves perform an operation.
Which is the best approach?
The problem cannot be eliminated by simple "refresh" approach(option 2).
Let's say both A and B viewing the table. Now A deleted a row, if you refresh the page of A it'll be only updated on A's view window. But code running on B will not be aware at the moment of the fact that the row is deleted. He will see the update only after he delete one row as per option 2 or obviously make a browser refresh.
One quick solution is to refresh the table data by using Ajax request in a fixed interval. For ex: 5 seconds.
To make the table reflect truly real-time data of the database at any moment for every user you have to use websocket. For this you need both real-time server and client.
You can implement a portion or feature of your entire web application in something like Socket.IO to make that portion real-time while keeping the other portion as is using your existing technology stack.
I am working on an angular web application where I want to show the user messages about different actions (like success, failure, etc.). These messages are set to clear automatically after 10 seconds, or when the user clicks the x button beside every alert. This is the easy part.
The difficult part is - I want to maintain these messages when the user navigates to different parts of the application. Something like facebook/google chat boxes.
I initially went through some ideas and decided on a solution where I stored the messages in an array in the local storage and every web page had code to look for these messages and display them if found.
But, I faced issues with the timing of the messages disappearing automatically (the timer reset to 10 seconds for all messages on page load). And also whenever the URL changed, the messages would go away and would load as new alerts after the new web page finished loading.
What is the correct way of doing this?
Whenever an alert appears in your application, you can store its timestamp together with the message. And on the page load call setTimeout for each alert with a callback of removing this message and the time difference between now and the timestamp+10sec.
Using your implementation, the timeout issue is easy to fix: when you store the alert, include an epiresAfter property as well. This property would be a timestamp af when you expect the the alert to no longer display.
Then you set up a timeout that either:
polls and checks the timeout, clearing it if the current time is past the expiration time
or a timeout that triggers after the difference between the expiration time and the current time.
I would consider treating your Message component as a singleton, i.e. it only ever has one instance. If the Message component is a child to other components, then it will be removed when it's parent component is removed from the DOM.
Consider moving your messages component to the root/app level (i.e. inside your AppComponent). This way as the user navigates around your app, the Messages component will always be visible. Further, the state of the notifications can be stored within this Messages component, so it's own timer wouldn't be affected by the rendering/removal of other components.
This all assumes you aren't using some sort of global store for your app state. If you are, consider Hyun Woo Krassilchikoff's answer which details implementing a global NGRX store.
The most efficient way is actually to use an NGRX store for your error message and a unique component in the main layout of your application rendering any message streamed from the store.
You'd need:
an action for pushing any kind of alert:. E.g. PushAlert
an action for clearing the alert. E.g ClearAlert
a reducer for managing both actions above
a selector to stream the portion of the state related the alerts
an effect which triggers ClearAlert 10 sec after detecting PushAlert
a component which displays the alert streamed by the store
Let's say we have a web app that only has one element, for example an image IMG1 and if an user clicks on it, it will change to another image IMG2 (this change should be visible only to the users that clicked and triggered the event).
Then, I have another event that triggers when a total of 100 users clicked on the image (or any other back-end related event), and this time I want to dynamically change the image to IMG1 (but now I want the change to happen and be visible to all the users of the website).
The confusion starts when I realise that for both events the function would be the same (changing the src of that HTML img element) yet I want it to have a different effect:
on the event of a user click change it for that user only.
on an outside event that doesn't involve a specific user, change it for all the users to see the same image.
How does this work? what is the thing that makes the difference between a HTML change that only affects the users locally (on their actions) and a change that has a global effect (to all the users).
UPDATE !!!
I should have been more specific with what I don't know.
I'm familiar with AJAX request and I already have the backend sorted.
In the frontend script I have an event listener for the event from the backend, and all my questions are actually about 'what and how to do it' after the event listener is triggered.
Now, what I want to do when this happens is to make some changes, the main one being to change that image IMG1 to IMG2 for all the users (as it would be a dynamic update to the website) but also:
I need that change to be permanent, so in a case of users reloading the page or new users coming in, they all should still see IMG2. (And the only time the image would change would be when the event listener on the frontend script will trigger again on the same backend event to change the image again (to IMG3) for example. And yes, in this example there is NO 'on click' request for the users to change the image, so ignore my example previous to the update.
Now to address your answers, I checked the web sockets stuff and it seems to be doing what I need if I run that 'on event' change of image to all sockets. Which only leaves me with 2 questions now:
1) Will this change that occurs on all sockets to change the image be permanent, so in a case of users reloading the page or new users coming in, they will all see the new image (IMG2) as a permanent change to the webpage ?
2) Regarding these type of permanent changes, isn't reactJs a way of doing such changes dynamically?
What would actually happen if on that event listener (for the backend event) I simply ignore all the web sockets stuff and run the same code of changing the src of the image ?
2.5) Because from how I see it, that event in the backend fires without any specific user input, thus is not linked with any user. So if I simply run the code on that event without websockets It should either do absolutely nothing (so no change for anyone) OR do the change for all the users (acting simply as a dynamic update to the webpage). How does this work?
I'm looking forward for your answers, and thank you all in advance!!!
The click event needs to be handled by an AJAX request, sending a message to the server and the server will handle that and respond. Upon the response, the first type of event is executed for the user.
On server-side you will need to have an event queue somewhere, maybe in the database. If you are using WebSockets, then you will have to execute the second type of event for all users if the request is met via WebSocket channels. If you are not using WebSockets, you will need to do polling from the browser. Anyway, you will need a counter on the server-side to be able to determine when the second type of event is met.
EDIT
Yes, WebSockets are the way to go unless there is a strong reason not to do so, like a boss saying he or she does not want the server to use WebSockets. With WebSockets you have a live channel between the server and the client browsers. You can use this channel to send the URL change to the client. On the other hand, the client will have to handle the change with Javascript, gathering the tags where the src is to be changed and change them. If you happen to have a class of changable for all such tags, then executing the change can be done with a function like this:
function changeSources(newSrc) {
var items = document.getElementsByClassName("changable");
for (var index = 0; index < items.length; index++)
items[index].src = newSrc;
}
However, this change will be effectuated only for the loaded page which was initially loaded and upon new loads, this, by itself will not use the new src. So you will have to solve that problem as well. A neat way to do it is to store the new src on the server before you send it out to the client via WebSocket and use this stored src as the src of those tags when the client requests for the HTML. So, your problem has two parts, the first is changing the src on already loaded pages and the second is making the change permanent.
ReactJS is a Framework. At this point we need to define the technical background, since ReactJS will use a possible solution from these.
WebSocket
https://www.npmjs.com/package/react-websocket
This is a WebSocket implementation. The best technical background here is to use WebSockets unless there is a very good reason not to do so.
Server notification system
https://www.npmjs.com/package/react-notifications
Server notification systems in general are one-way ticket roads. The server may send a notification, but the client has no such possibility.
Polling
The browser may periodically send HTTP requests to the server and this way it can receive the src change response. This is a good solution if WebSockets and server notification systems are not an option.
Forever frame
You can use an invisible iframe to be loaded forever, which will provide you with the possibility of sending real-time messages from the server to the client, but this is very hacky.
The difference may be between a front end, running in the browser, or the mobile app, of each user, which is local, and the back end, where you can share data between all users.
This can be implemented by, for example, firebase. Here is an example: Firebase - Multiple users simultaneously updating same object using its old value
This does not mean, obviously, that back end data is always shared... In many cases each user accesses his own copy of back end data that is stored in a database.
I was making a feature with which you can swap the rows in table. It works great, but after refreshing the page, if I swapped some rows, the rows are back to the place, where they have been before swapping. I'd like to know, why is that happening and how to avoid that. This is the code of the feature, if someone needs it:
swapRows = function(rowTable) {
rowTable.find(".glyphicon-sort").click( function() {
row = $(this).closest("tr");
row.insertBefore( row.prev() );
} );
}
This is happening because after refreshing, your page returns to its initial state (from server, or cache), duh.
To retain the state, you have several options: AJAX (so that you don't have your page refreshed), or HTML5 history state manipulation, or local storage etc., all of which are beyond the scope of this question I'm afraid.
This is happening because jQuery is a client side technology and the data is not persisted between requests. When you refresh your page you make new request to the server, which in turn respond with a new page (.html) with a default state.
In order to save this data between requests you can use cookies.
I own a single page application implemented using AngularJs.
Let's suppose my application has 2 pages, sharing the same main top-navbar.
The navbar should display (among other things of course) the current number of unread messages addressed to the connected user, like this screenshot shows:
In my current implementation, while AngularJs starts to be loaded, I trigger in the run method an ajax query to get all the current user's messages (of course, just after the query aiming to check if any user is authenticated).
However, as $http.get is perfectly asynchronous by nature, it is frequent that there is a delay of one or even two seconds (in the worst case, if there are many messages and an additional logic) between the navbar display and the update of number of messages:
............ 1 second later ......
If the icon was a specific page content, I could use the resolve property of the template, but in this case, the navbar is shared by each page.
Should I display some icon representing a "load" of messages while messages are queried to inform user that some messages may be present?
How to deal with this case?
It seems to be the main drawback of single-page-application IMHO.
Indeed, in a non-single page application, the query could happen at server side before server before sending the web page to the client, leading to no delay at all.
I see this as the beauty of single page apps. Rather than waiting seconds for a page to load messages (on the server) that I might not be interested in, it can be lazy loaded giving me instant feedback to perform my desired task.
I would typically go with a directive for something like this and stay off $rootScope, but it sounds like ngShow/ngHide would be able to tie in directly to your setup:
HTML/JADE (classes are just an example)
div#messages(ng-show='messages')
i.fa.fa-envelope-o
span.badge {{messages.new.length}}
div#msgLoading(ng-hide='messages')
Javascript/Angular
.run(function($rootScope, $http, ...
...
//$rootScope.messages has not been instantiated yet
$http.get('//someendpoint').then(function(results) {
$rootScope.messages = results.data;
});
...