Single Page Application / Confused with the loading of main navbar's contents - javascript

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;
});
...

Related

Keep alert messages showing between different web pages

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

What is the right way to call dynamic content (currently using ajax) inside a cached page?

We have a news website where we cache a complete article page.
There are 4 areas that need to continue to be dynamic on that page:
View Counter: We add +1 to view_counts of that article when page loads.
Header: On the header of the website we check if session->id exists or not if it does we display a Welcome [Name], My Profile / Logout and if not we show Register / Login.
Comments: We display the comments made for that article.
Track User Behavior: We track every single action made by users on the site
Now the only way we could think of doing this is through AJAX calls:
$('#usercheck').load(<?php echo "'" . base_url() . "ajax/check_header'"; ?>);
And so on.
This is creating a massive load on CPU, but what would be the right/alternative way of approaching this?
Please see attached:
First of all, you do not have to use AJAX for every possible dynamic content, especially in the case of comments, you may as well load them via an iframe.
That way, you are not relying on Javascript to make the request.
It may even work for the counter.
However, you problem is not Javascript, nor the database server, based on what I can see from your graph. It seems to me you have some heavy PHP controllers, maybe you are loading a heavy framework just to have $session->id checked.
Further, what do you mean by "we track every single action"? How do you track them? Are you sending an AJAX request from every little thing or are you debouncing them with JS and only sending them one every 30 seconds or so?
My advice is that you consider the size of the PHP code you are calling, and slim it down as much as you can, even to zero if it seems feasible (by leveraging localStorage to keep track of you user session after the first login), and maybe loading the counter and the comments in alternative ways.
For example, I infer you are only checking the counter once per page load, ignoring subsequent loads by other users while the current user is reading the article, so your counter may happen to be out-of-date once i a while, depending on your traffic.
I going to explain it better: your page has n views, so when I load it, you request for n and then display n+1 to me. While I'm reading, the same page gets requested and viewed x times by other users. Your counter on the server has been surely updated to n+x, but the counter on my page still says "n views".
So, what's the point in being picky and showing n+1 to me and the not updating it, thus being off by x?
So, first of all the counter controller should be as slim as possible, and what if you loaded it within an iframe, auto updating without AJAX?
How to refresh an iframe not using javascript?
That would keep the counter up-to-date, you may render it with PHP just once per page view, and then just statically serve the resulting HTML file.

mithril avoiding to reload image

I am using mithril 0.2.2-rc.1. I saw in the routing documentation:
Routing is a system that allows creating Single-Page-Applications (SPA), i.e. applications that can go from one page to another without causing a full browser refresh.
Indeed when I am routing to the same page with different parameter only the part that I want to change is refresh expect this :
m("img[src='assets/images/logo.png'][alt=''][width='100']")
I can see in the network communication that the image is reloaded (another GET request).
Is there a way to avoid that?
route.js
m.route.mode = "pathname";
m.route(document.getElementById('app'), '/', {
'/': main,
'/modelling/:level': main
})
It's difficult to see how the two pieces of code fit together, but two things suggest themselves:
Every change of route (even if that change results in the same route entry, eg /modelling/x to /modelling/y) will result in the entire DOM being regenerated. You can prevent this behaviour by calling m.redraw.strategy( 'diff' ) in each route component's controller.
Repeatedly requesting the same resource does not lead to extra calls to the server: a multi page site with every page requesting the same JS and CSS will only load those resources once, and will hit browser cache on subsequent requests. Thus repeatedly asking for the same image resource will not generate any new calls to the server.
If you check the documentation of the m method you will see that the config attribute lets you retain elements across redraws. So this should work for you:
m('img', {config: function persist(el, isInit, context)}) {
context.retain = true;
}

Split-Panel AJAX-Enabled ASP .NET MVC 5 Application With History.js

I'm trying to use ASP .NET MVC 5 to create a Web application where I have a 'main' page split into two panes.
Each of these two panes is supposed to show PartialViews (sometimes lists, sometimes data, almost like a master-detail flow pattern) that may contain links. These links, make AJAX calls to get PartialViews from the server and replace the content of one of the panes (either the same one where the link was, or the opposite one). These PartialViews may represent different ViewModels and come from different controllers.
I can get this to work by using custom jQuery/Javascript. The links on the PartialViews trigger functions that fetch other PartialViews and replace a Pane's content. My problem arises when I want to allow users to use the browser's back button.
In order to do that for both HTML5 and non HTML5 browsers, I've added History.js to my project and configured my application so that it uses pushState() with the fetched data and the url to get it, etc.
History.pushState({
content: [THE_PARTIAL_VIEW],
paneId: [THE_ID_OF_THE_PANE_TO_REPLACE],
paneUrl : [THE_PATH_TO_GET_THE_PARTIAL_VIEW]
},
title,
[THE_URL_TO_SHOW_IN_THE_BROWSER_BAR]);
Given that:
The main page is delivered by the `HomeController' and that all other PartialViews are delivered by other Controllers;
Those .cshtml files are stored under Views/CONTROLER_NAME;
I want the user to be able to use back/forward button navigation;
I want the user to be able to, for example, bookmark or refresh the [THE_URL_TO_SHOW_IN_THE_BROWSER_BAR] and get the main page with the corresponding panes;
my main issue is being able to capture the 'state' of a page, represented by the information in panels 1 and 2 which may come from different Actions in different Controllers, in a url.
What is the recommended approach for me to 'build' the [THE_URL_TO_SHOW_IN_THE_BROWSER_BAR] url that shows on the browser's top bar and for my application to understand that url (in case the user saves/refreshes/sends it) and deliver a main page with the appropriate panels, taking into account that their contents may come from different Controllers?
My Suggested Approach...
is to use the HomeController all the time to deliver the main page and then have the other Controllers contain the Actions for returning PartialViews.
HomeController would then have a "Main" Action that can take parameters detailing the names (possibly shortened) of the controller/action/etc. needed for making the left and right pane. The "Main" action would then return the main View with a ViewModel containing those attributes and then Razor would render the necessary Partials using Html.RenderAction(...) and the received parameters.
My javascript should keep track of what's being displayed on each pane and update the url's parameters accordingly.
As there were no answers/comments, my Suggested Approach is to use the HomeController all the time to deliver the main page (MainView) and then have the other Controllers contain the Actions for returning the different PartialViews.
HomeController has a "Main" Action that can take parameters detailing the names (possibly shortened) of the controller/action/etc. needed for making the left and right pane. The "Main" action returns the main View with a ViewModel containing those attributes and then Razor renders the necessary Partials using Html.RenderAction(...) and the received parameters.
My javascript keeps track of what's being displayed on each pane and update the url's parameters accordingly.

MVC3, switching between Views in the same page

I am working on a MVC project that is supposed to have one page only, with two zones. On one zone I have a Google Map with markers, and the second zone is populated with the selected marker's details.
The details view has a button that when clicked should change the entire view into edit mode without refreshing the page or redirecting it. I have used two views, for details and edit and with the help of ajaxForm function I am switching back and forth between these two views. I'm adding the ajaxForm on documentready for edit view.
<script type="text/javascript">
// wait for the DOM to be loaded
$(document).ready(function() {
// bind 'myForm' and provide a simple callback function
$('#currentDiv').ajaxForm(function(data) {
$('#currentDiv').html(data);
});
});
</script>
The problem appears when on server-side an error appears while trying to save data from edit view and I want to return to the same edit view with the errors displayed. The ajaxForm handler is not added any more and even if the new values that will try to be saved are ok, the detail view is loaded in another page.
Unfortunately, the use of ajaxForm creates some other problems because I don't have control over the cases when the ajax call fails.
Any ideas on how could I fix this? Is it some other solution to switch between those two views without using ajaxForm and without refreshing the page?
I think there are a couple of different questions that you are asking.
First off, you add jquery handlers to deal with the case when you get a 500 type error from the server.
Something like the following. I suggest taking a look at the docs for more info.
$(document).ajaxError(function(event,jqXHR,ajaxSettings,thrownError){
if (jqXHR.status != 0){
window.location = <error page>
}
}
The second problem seems to stem around error handling of known errors (say invalid input). In this case I suggest the following workflow.
1) User clicks on edit button, taken to edit screen
2) User enters in data, use client side validation to do initial check
3) User submits, user is then taken to the view screen and is shown a
success or error message.
The server response could look like:
public ActionResult Edit(EditModel model){
if (!ModelState.IsValid)
{
return Json(new {successful = false, message = "Failed.."});
}
...
}
On the client side, your form callback should now handle the message and the fact it was successful or not. In my implementation, I used knockoutjs to create a "message" area that I could update and clear. (I created templates, etc).
Remember to use client side validation for the easy field validation stuff.... This will save a trip back to the server.
Yours could be quite simple, by popping up the message returned from the server.
Lastly, document ready only fires when the original document is done loading, never again for an ajax call (at least that is my understanding). Just put that code that is the document.ready at the bottom of the edit page. It will fire after the html it is targeting has already been rendered.
Hope that helps!
I have begun to move away from the asp.net views available in ASP.Net MVC due to some incompatibilities and/or unnecessary complexities when trying to achieve functionalities expected of AJAX enabled sites of the day.
I would recommend moving towards a design where you use "dumb" HTML files, use jQuery to download them using AJAX and drop them into a container (personally I use a div) and then use another AJAX call to gather the data from a controller. There are a number of advantages to this approach:
It establishes a real (not fake) separation between client side and server side code.
Html files can be cached on the client cutting down on the amount of data transmitted.
Binding of the Html elements becomes a client side task achieved using jQuery offloading processing cycles from the server.
Controllers essentially become collections of web methods which means they can be untilized by iPhone and Android apps making mobile deployment easier.
I realize this probably isn't the exact answer you're looking for and this may not be an option for you but my hope is that it will help someone at some point make a decision to move away from mixing HTML and server side code.

Categories