Im developing a real time application using SignalR, im using MVC and Angularjs (SILO). Each cshtml page is a SPA. i have an angularjs common service which contains generic operations like delete, post etc.. Also in my common service i introduced signalR. The problem im having is that since the Serivice is only loaded when a SPA loads, so signalR only works in that SPA. What i want is my signalR to be available in my entire application.. what can i do to achieve this?
My Common Service Code
self.hub = $.connection.MyHub;
var initializeSignalR = function () {
self.hub.client.addIssue = function (issue) {
alert(JSON.stringify(issue));
};
$.connection.hub.url = "https://sitename.azurewebsites.net/signalr";
$.connection.hub.logging = true;
$.connection.hub.start().done(function () {
var tenant = sessionStorage.getItem("tenantName");
if (tenant !== "") {
self.hub.server.subscribe(tenant);
} else {
console.log("Error occured");
}
});
};
initializeSignalR();
And on my SILO i have this
viewModelHelper.hub.on('addIssue', function (issueItem) {
issues.push(issueItem);
});
viewModelHelper is my service, lets say if im in SPA Home, signalR methods in SPA Contacts are not excecuted, i understand why, Its because my service is not initialized in that SPA. How can i achieve signalR availability throughtout my app even when the SPA is not loaded.
I think you will have two options:
Load it again in every SILO by placing your SignalR service reference in shared layout. This can keep your current application structure.
Change your app to become real Single Page App(SPA) then you can load and reuse just one SignalR connection in entire app. But this will changed so much of your current system. In that way, you can not use cshtml as current, all view layer and some part of logic will be pushed to client side.
Related
In fully server side based rendering (non Web 2.0), deploying server side code would directly update client side pages upon page reload. In contrast, in React based Single Page Application, even after React components were updated, there would be still some clients using old version of the components (they only get the new version upon browser reload, which should rarely happen) -> If the pages are fully SPA, it's possible that some clients only refresh the pages after a few hours.
What techniques should be employed to make sure the old components versions are not used anymore by any clients?
Update: the API doesn't changed, only React Component is updated with newer version.
You can have a React component make an ajax request to your server, when the application loads, to fetch "interface version". In the server API, you can maintain an incremental value for the client version. The React component can store this value on the client (cookie/local storage/etc). When it detects a change, it can invoke window.location.reload(true); which should force the browser to discard client cache and reload the SPA. Or better still, inform the end-user that a new version will be loaded and ask them if they wish to save the work and then reload etc. Depends on what you wanna do.
Similar to Steve Taylor's answer but instead of versioning API endpoints I would version the client app, in the following way.
With each HTTP request send a custom header, such as:
X-Client-Version: 1.0.0
The server would then be able to intercept such header and respond accordingly.
If the server is aware that the client's version is stale, for example if the current version is 1.1.0, respond with an HTTP status code that will be appropriately handled by the client, such as:
418 - I'm a Teapot
The client can then be programmed to react to such a response by refreshing the app with:
window.location.reload(true)
The underlying premise is that the server is aware of the latest client version.
EDIT:
A similar answer is given here.
What techniques should be employed to make sure the old components
versions are not used anymore by any clients?
today (2018), many front apps use service workers. With it, it's possible to manage your app lifecycle by several means.
Here is a first example, by using a ui notification, asking your visitors to refresh webpage in order to get latest application version.
import * as SnackBar from 'node-snackbar';
// ....
// Service Worker
// https://github.com/GoogleChrome/sw-precache/blob/master/demo/app/js/service-worker-registration.js
const offlineMsg = 'Vous êtes passé(e) en mode déconnecté.';
const onlineMsg = 'Vous êtes de nouveau connecté(e).';
const redundantMsg = 'SW : The installing service worker became redundant.';
const errorMsg = 'SW : Error during service worker registration : ';
const refreshMsg = 'Du nouveau contenu est disponible sur le site, vous pouvez y accéder en rafraichissant cette page.';
const availableMsg = 'SW : Content is now available offline.';
const close = 'Fermer';
const refresh = 'Rafraîchir';
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
function updateOnlineStatus() {
SnackBar.show({
text: navigator.onLine ? onlineMsg : offlineMsg,
backgroundColor: '#000000',
actionText: close,
});
}
window.addEventListener('online', updateOnlineStatus);
window.addEventListener('offline', updateOnlineStatus);
navigator.serviceWorker.register('sw.js').then((reg) => {
reg.onupdatefound = () => {
const installingWorker = reg.installing;
installingWorker.onstatechange = () => {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
SnackBar.show({
text: refreshMsg,
backgroundColor: '#000000',
actionText: refresh,
onActionClick: () => { location.reload(); },
});
} else {
console.info(availableMsg);
}
break;
case 'redundant':
console.info(redundantMsg);
break;
default:
break;
}
};
};
}).catch((e) => {
console.error(errorMsg, e);
});
});
}
// ....
There's also an elegant way to check for upgrades in background and then silently upgrade app when user clicks an internal link. This method is presented on zach.codes and discussed on this thread as well.
You can send app’s version with every response from any endpoint of your API. So that when the app makes any API request you can easily check there’s a new version and you need a hard reload. If the version in the API response is newer than the one stored in localStorage, set window.updateRequired = true. And you can have the following react component that wraps react-router's Link:
import React from 'react';
import { Link, browserHistory } from 'react-router';
const CustomLink = ({ to, onClick, ...otherProps }) => (
<Link
to={to}
onClick={e => {
e.preventDefault();
if (window.updateRequired) return (window.location = to);
return browserHistory.push(to);
}}
{...otherProps}
/>
);
export default CustomLink;
And use it instead of react-router's Link throughout the app. So whenever there's an update and the user navigates to another page, there will be a hard reload and the user will get the latest version of the app.
Also you can show a popup saying: "There's an update, click [here] to enable it." if you have only one page or your users navigate very rarely. Or just reload the app without asking. It depends on you app and users.
I know this is an old thread, and service workers are probably the best answer. But I have a simple approach that appears to work:
I added a meta tag to my "index.html" file :
<meta name="version" content="0.0.3"/>
I then have a very simple php scrip in the same folder as the index.html that responds to a simple REST request. The PHP script parses the server copy of the index.html file, extracts the version number and returns it. In my SPA code, every time a new page is rendered I make an ajax call to the PHP script, extract the version from the local meta tag and compare the two. If different I trigger an alert to the user.
PHP script:
<?php
include_once('simplehtmldom_1_9/simple_html_dom.php');
header("Content-Type:application/json");
/*
blantly stolen from: https://shareurcodes.com/blog/creating%20a%20simple%20rest%20api%20in%20php
*/
if(!empty($_GET['name']))
{
$name=$_GET['name'];
$price = get_meta($name);
if(empty($price))
{
response(200,"META Not Found",NULL);
}
else
{
response(200,"META Found",$price);
}
}
else
{
response(400,"Invalid Request",NULL);
}
function response($status,$status_message,$data)
{
header("HTTP/1.1 ".$status);
$response['status']=$status;
$response['status_message']=$status_message;
$response['content']=$data;
$json_response = json_encode($response);
echo $json_response;
}
function get_meta($name)
{
$html = file_get_html('index.html');
foreach($html->find('meta') as $e){
if ( $e->name == $name){
return $e->content ;
}
}
}
Yes in server side rendering if you need to update a small part of the page also you need to reload whole page. But in SPAs you update your stuffs using ajax, hence no need to reload the page. Seeing your problem I have some assumptions:
You see one of your component got updated but other components getting data from same API didn't update. Here comes Flux Architecture. where you have your data in store and your component listen to store's changes, whenever data in your store changes all your components listening to it's change will be updated (no scene of caching).
Or
You need to control your component to be updated automatically. For that
You can request your server for data in specific intervals
Websockets can help you updating component data from server.
I'm very curious and very much in need of some pointers.
Basically I have a web application that invokes a hub method in .NET (c#)
var hubConnection = new HubConnection("http://domain/signalr");
IHubProxy applicationHubProxy = hubConnection.CreateHubProxy("ApplicationHub");
hubConnection.Start().Wait();
applicationHubProxy.Invoke("JoinGroup", appKey).Wait();
And ideally if a client (another application hosted else where) has a connection to the hub then receive the message in js.
var connection = $.hubConnection();
connection.url = "http://domain/signalr";
// Declare a proxy to reference the hub.
var applicationHub = connection.createHubProxy('ApplicationHub');
//mock is just a dynamic type that recieves the message in the join group hub method
applicationHub.on('mock', function (msg)
{
alert(msg);
});
Please note that I'm already using Cors, and singalr is working fine with Tests. As I begin with the production code, I'm hoping I could get a better understanding of how the above is going to work.
So just to reiterate, how do I receive a response in JavaScript that is sent from another app in c#?
Any help greatly appreciated,
I am interested about React.js for server side rendering for my web service, but I encounter one problem trying to port from my web service developed using Express.js. Currently, my web service is serving 2 platform
web (Angular.js client)
Android (native application - developed in Java)
My current architecture is all my client side are connected to my server using JSON. So, my client will send a HTTP GET/POST request to my server and I will return JSON back. My server doesnt server HTML page.
Below, are examples of my endpoints APIS using REST:
/api/books
GET - return JSON with array of books information
POST - send a JSOn to server, to add new book information
How should I change my server side to include React? Since React uses server side rendering instead of communicating using JSON?
My current Angular.js project, I placed them into a public folder and it serves as static files.
Possible solutions:
should I add a new set of URLs, one for rendering (/books), the second for JSON (/api/books)?
server detect the type of application (mobile or web) and serve accordingly?
UPDATE 1
Another possible solutions
res.format({
'text/html': function() {
var html = React.renderToString(React.createElement(BooksPage, {books: books}));
res.render('main-layout', {
html: html,
data: JSON.stringify({books:books})
});
},
'application/json': function() {
res.send({
book: books
})
}
})
is to use Express.js content negotiation (res.format), doing so i keep the same route for handline HTML and JSON?
You have your api routes, and you have your page routes. The user doesn't go to http://example.com/api/books to see a list of books, they go to something like http://example.com/books/ to see the books page.
When you get a request for a page like /books/, you serve html. Your api can still be used on the client.
The simplest way to do this is make very lean controllers, and keep most of the logic in plain functions (example with promises, but not important).
function getBooks(){ return /* database thing returns promise */ };
app.get('/api/books', function(req, res){
getBooks().then(function(books){ res.send(books); });
});
app.get('/books/', function(req, res){
getBooks().then(function(books){
var html = React.renderToString(React.createElement(BooksPage, {books: books}));
res.render('main-layout', {
html: html,
data: JSON.stringify({books:books})
});
});
});
Let's say your main-layout template ends up producing this:
<html><head></head>
<body>
<div id="root">
<!-- output of renderToString -->
<div data-reactid="0">...</div>
</div>
<script>
/* output of the JSON.stringify */
var __initial_data = [{"id": ...}];
</script>
<script src="client-side-code-bundle.js"></script>
</body>
</html>
You pick up the rendering on the client using the same data as the server used:
React.render(React.createElement(BooksPage, __initial_data));
Your event listeners are bound, and you go from there.
I am an IT student and I'm learning how to use Backbone.js. I read all the documentation but I find it easier to learn when I use example apps,because I never have been programming this type of apps,so it was hard and confusing to think of a way to build my own app, so I used https://github.com/dperrymorrow/example-backbone-app to make similar edited app. The example app doesn't have a server side.
Now I need to connect the app to use parse.com as a backend(server-side) instead to use local collection.
If someone could please tell me what should I change and transform in the code so it connects example app to parse.com app with REST API so when I edit something in the app to be syncronized with parse.com.
I will be really grateful if someone is willing to explain this in a more descriptive way than saying :"you should read documentatin" because I did,and I still don't get the point :)
Have a nice day.
It's just about having the right backbone models and collections and settings the right url on the collection and urlRoot on the model. Then you can just can just call backbone methods like sync, save or delete.
Best detailled answer covering also the REST explanation probably is this one.
Cant you just swap the backbone collection and model to Parse's ones?
Parse.com is a webservice providing REST interfaces for anything you like, Lets connect that to our Backbone models.
First of all Lets create a new app on Parse.com, mine is called FunkyAppartments.
Insert the script tag for loading Parse javascript lib into index.html or whathever:
<script src="http://www.parsecdn.com/js/parse-1.5.0.min.js"></script>
Switch the backbone model and collection to use parse types instead (and rename the fetch method if you have extended backbones, since we do not want to overide the one of parse):
//var Appartment = Backbone.Model.extend(); Backbone wo. Parse.com
var Appartment = Parse.Object.extend("Appartment");
//var Appartments = Backbone.Collection.extend({ Backbone wo. Parse.com
var Appartments = Parse.Collection.extend({
model: Appartment,
initializeData: function(){
var self = this;
var callback = function (data){console.log(data); self.reset(data)};
S.Appartments.loadAppartments(callback);
},
loadAppartments: function(callback){
debugger;
this.query = new Parse.Query(Appartment);
var result = this.fetch();
callback(result);
return result;
}
});
I added a debugger tag in the load appartments so that developer tools breaks in the middle of the controller, here I have access to the Appartment private type of the controller, hence i can store some data on the parse server and verify by pasting the below in the developer tools console.
var testAppartment = new Appartment();
testAppartment.save({name: "foobars"}).then(function(object) {
alert("yay! it worked");
});
Yei, the data shows up in the parse.com UI for the app we just added there. And more importantly it shows up in our frontend. That was easy!
I'm using this code to get client information :
$.getJSON("http://www.geoplugin.net/json.gp?jsoncallback=?",function (data) {
console.log(data.geoplugin_request);
console.log(data.geoplugin_countryName);
});
Then I would like to record this information at the first time that client visit the website (session start of the website). My current project are using backbone.js, require.js, underscore.js.
Any suggestions would be appreciated. Thanks.
Assuming that you have application.js file which act as a entry point of the backbone aplication which initializes your router an all stuff, you can set the client details in the browser using localStorage.
// Retrieve the object from storage
var retrievedVar = localStorage.getItem('countryName');
if( retrievedVar == null) {
$.getJSON("http://www.geoplugin.net/json.gp?jsoncallback=?",function (data) {
console.log(data.geoplugin_countryName);
// Put the object into storage
localStorage.seItem('countryName', JSON.stringify(data.geoplugin_countryName)
});
Hence the getJSON will only be fired once when localStorage var is not set.