Close socket.io connection from client on Url change - angularjs

I am using Socket.IO to pull data for plotting graphs, on
var socket = io.connect();
my Node js server's io.on('connection', function(socket){}) starts a function, on interval of every 1 sec it emits the data to the clients connected.
I want the socket to close on change of Url (away from app) from client and server should stop emitting data.

As we discussed in the comments to the question, you're looking to disconnect from the socket when your Angular ngRoute controller partial route changes away from a given state; this is different as a partial routing single-page application doesn't trigger a fully new page, but rather loads up partial content (defined in partial/template files or script definitions). This is key to why Furkan Başaran's answer won't work for you out of the box for changes between Angular routed states within the same SPA.
I've prepared a plunker that is a fairly basic AngularJS app; it has three html partials that it shows at three corresponding routes using ngRouter and invokes a function to <do-something> when the route change occurs. In this case, it's throwing an alert function, whereas in your case you may which to check for whether or not the route is to anything not the route you want to provide the socket functionality on.
Note: there is a change on every time the Angular app registers a route changes, so the initial establishment will register with a blank value for the / state, then every time it changes from the load, including the otherwise.redirectTo('/destination1').
Plunker (embedded) link:
http://embed.plnkr.co/ayjgYCsox7RGl5OjyGsV/
Quick break down:
I start by defining my handling function to be triggered on ngRouter changes. The passed value is the registered route (after the / or /index.html), such as /potatoes as is a case in my example. This is where you should perform your socket.disconnect();.
function changedMyPartialRoute(val){
alert('url has changed to: ' + val);
}
After I defined my app (angular.module('appName', [...), I define the config for my ngRouter setup.
.config(['$routeProvider', '$locationProvider',
function ($routeProvider, $locationProvider) {
$routeProvider
.when('/meat', {
templateUrl: 'meat.html',
controller: 'MeatCtrl',
controllerAs: 'meat'
})
.when('/potatoes', {
templateUrl: 'potatoes.html',
controller: 'PotatoCtrl',
controllerAs: 'potato'
})
.when('/bacon', {
templateUrl: 'bacon.html',
controller: 'BaconCtrl',
controllerAs: 'bacon'
})
.otherwise({
redirectTo: '/meat'
});
}])
Finally, I invoke a run block on the app (module) to provide that hook into detecting the change. I'm passing in $rootScope and $location and it performs a $watch on the $rootScope to detect a change in the $location.path(). That whole run block:
.run( function($rootScope, $location) {
$rootScope.$watch(function() {
return $location.path();
},
function(val){
// the handling function from step 1
changedMyPartialRoute(val);
});
})
If you need to do something with $rootScope, as I suspect you might, you'll need to hand off the $rootScope handle through the changedMyPartialRoute function or just define your handling inline in the function callback in the .run block. This HTML5rocks.com tutorial shows their configuration in their Angular app by hooking into $rootScope, as opposed to $scope as you mentioned in the above comments.
I hope that helps!

When client closed connection close automatically, if you want see this, write this code to your node server and look to console when closed your client tab.
io.on('disconnect', function() {
console.log("Connection disconnected");
})

Related

$location.path or $location.url doesn't trigger the ngRoute controller

I have a route defines as follows:
$routeProvider.
when('/projects/', {
controller: 'ProjectCtrl',
controllerAs: 'project_ctrl',
templateUrl: '/static/app/partials/project.html'
}).
After the login finishes I need the user to land on this link, hence in my controller I am using this:
vm.login = function(form) {
if (form.$valid) {
loginService.login(vm.loginFormData.username, vm.loginFormData.password);
loginService.setUpUser()
$location.url("/projects");
}
}
But unfortunately the controller associated with this view is not triggered, that is ProjectCtrl is not triggered. However when I click on the navigation link which uses in the dom, it works fine. Can someone please guide me here, may I am missing something conceptual.
Hence the larger question is how do I redirect a user in the controller using some APIs which also complies with ngRoute based controllers.
Try removing the last / in url so it matches $location.url("/projects");
$routeProvider.
when('/projects', {

$routeProvider , Clicking same link more than once

Below is my partial angularJS code which implements routeprovider
TicketApp.config(function ($routeProvider) {
$routeProvider
// route for Create Ticket Page
.when('/CreateTicket', {
templateUrl: '../Ticket/Create',
controller: 'CreateCtrl'
})
// route for Open Ticket Page
.when('/OpenTickets', {
templateUrl: '../Ticket/MyTickets',
controller: 'MyTicketsCtrl'
})
// route for All Users Search Page
.when('/SearchUsers', {
templateUrl: '../Account/AllUsers',
controller: 'AllUsersCtrl'
})
..........
When user clicks SAME link more than once, only the
first click makes an AJAX request ,the subsequent requests
do not.
What needs to be modified so that subsequent requests to SAME link also make an AJAX call.
EDIT :
I also added below code
TicketApp.run(function ($rootScope, $templateCache, $location) {
$rootScope.$on('$routeChangeStart', function (event, next, current) {
$templateCache.remove(current.templateUrl);
});
});
did not work for me.. :(
Why do want to execute Ajax requests? It is only one request required to load the template from the server. There is no need to load it multiple time, because it shouldn't change or your architecture is not really compliant to Angular.
You could probably invalidate the templateCache and force Angular to reload the templates.
Might be this will work for your case .When the user will click the Button(the ajax call start's) you just hide it(button) once the AJAX call return a response back then show it(button).

Need to resolve a $http request before the execution of the resolve property inside $stateProvider

I’m building an angular application that is going to run on several domains. Since there are different configurations on each domain I'll need to fetch all the variables by doing a call to the server. The call will return a JSON object that contains different rest urls.
My problem is that I need to do this call before the 'resolve' step inside the $stateProvider, since I already have a task that is dependent on the configuration object from the server.
What should work here is a really great feature $urlRouterProvider.deferIntercept(); documented here:
$urlRouterProvider
The deferIntercept(defer)
Disables (or enables) deferring location change interception.
If you wish to customize the behavior of syncing the URL (for example, if you wish to defer a transition but maintain the current URL), call this method at configuration time. Then, at run time, call $urlRouter.listen() after you have configured your own $locationChangeSuccess event handler.
The code snippet from the API documentation:
var app = angular.module('app', ['ui.router.router']);
app.config(function($urlRouterProvider) {
// Prevent $urlRouter from automatically intercepting URL changes;
// this allows you to configure custom behavior in between
// location changes and route synchronization:
$urlRouterProvider.deferIntercept();
}).run(function($rootScope, $urlRouter, UserService) {
$rootScope.$on('$locationChangeSuccess', function(e) {
// UserService is an example service for managing user state
if (UserService.isLoggedIn()) return;
// Prevent $urlRouter's default handler from firing
e.preventDefault();
UserService.handleLogin().then(function() {
// Once the user has logged in, sync the current URL
// to the router:
$urlRouter.sync();
});
});
// Configures $urlRouter's listener *after* your custom listener
$urlRouter.listen();
});
And also, related to this question:
AngularJS - UI-router - How to configure dynamic views
There is working example - plunker
To make it clear, suitable for this use case, let's observe the code of the plunker.
So, firstly we can see the .config() phase. It does have access to providers but NOT to their services (e.g. $http). Not yet, services themselves will be available later...
app.config(function ($locationProvider, $urlRouterProvider, $stateProvider)
{
// this will put UI-Router into hibernation
// waiting for explicit resurrection later
// it will give us time to do anything we want... even in .run() phase
$urlRouterProvider.deferIntercept();
$urlRouterProvider.otherwise('/other');
$locationProvider.html5Mode({enabled: false});
$stateProviderRef = $stateProvider;
});
What we did, is set a reference to provider (configurable object), to be used later: $stateProviderRef.
And the most crucial thing is we STOPPED the UI-Router, and forced him to wait for us with $urlRouterProvider.deferIntercept(); (see the doc and cites above)
There is an extract of the .run() phase:
app.run(['$q', '$rootScope','$http', '$urlRouter',
function ($q, $rootScope, $http, $urlRouter)
{
// RUN phase can use services (conigured in config phase)
// e.g. $http to load some data
$http
.get("myJson.json")
.success(function(data)
{
// here we can use the loaded stuff to enhance our states
angular.forEach(data, function (value, key)
{
var state = { ... }
...
$stateProviderRef.state(value.name, state);
});
// Configures $urlRouter's listener *after* your custom listener
// here comes resurrection of the UI-Router
// these two important calls, will return the execution to the
// routing provider
// and let the application to use just loaded stuff
$urlRouter.sync();
$urlRouter.listen();
});
}]);
Most important is, that this .run() was executed just ONCE. Only once. As we require.
We can also use another technique: resolve inside of one super root state, which is parent of all state hierarchy roots. Check all the details here:
Nested states or views for layout with leftbar in ui-router?
There is another way how to solve the:
How to resolve $http request before the execution of the resolve property inside $stateProvider?
In case, that we just need to get some $http result inside of the resolve, we can do it just like this:
resolve: {
myResolve1:
function($http, $stateParams) {
return $http.get("/api/foos/"+stateParams.fooID);
}
}
This is a snippet from documenation of the [$stateProvider][1], section resolve. We can see, that we return the promise of the $http service: return $http.get()
So, to extend that, to asnwer:
But how can I make the ui-router wait until the promise is resolved?
we can just use return $http.get() and then .then(). And inside of it, we have access to returned result - which we can adjust:
myResolve1:
function($http, $stateParams) {
// we still return the promise of the $http.get))
return $http
.get("/api/foos/"+stateParams.fooID)
.then(function(response) {
// but now, the content of resolved property
// will be the first item of the loaded array
return response.data[0];
};
}
}
There is also enahnced solution - in case we need to make this to happen before every state. We just introduce some "root" state as a super parent. It will contain such resolve and all child states will wait until this one is resolved, but just resolved just once. See more here: angular ui-router resolve for parent state

AngularJS application architecture

I am relatively new to Angular but I am quite an experienced developer. So far I have made quite some progress in building my application to work with a CMS. I am a bit lost however on what the 'correct' approach would be to handle data in my model.
This is best described with an example:
Because I am hooking up my angular frontend with a CMS, the routing (pages) exist only in the CMS context. This means that the routing should be dynamic as well. I have managed to get the dynamic routes thing to work, but when I try to do things the right way (actually getting data from a server) I run into some issues...
app.config(function($provide, $routeProvider) {
$provide.factory("$routeProvider", function() {
return $routeProvider;
});
});
// Load the dynamic routes from the API...
app.run(function($routeProvider, $http, $scope, logger, siteRoutes) {
$routeProvider.when('/', { templateUrl: '__views/', controller: 'ContentPageController' });
$routeProvider.otherwise({redirectTo: '/'});
});
In other words, I inject a service into my app.run method (siteRoutes) and this one should connect to the API.
So my siteRoutes is a service:
cmsModule.service('siteRoutes', function siteRouteFactory(apiConnection, logger)
// SNIP
And in this service I inject my generic apiConnection service:
cmsModule.factory('apiConnection', ['$q', '$http', '$timeout', 'logger', function apiConnectionService($q, $http, $timeout, logger)
What I want is this:
I would like the siteRoutes service to load the data once and not execute the connection every time. I did this in the following way:
bla.service('example', function() {
var service = {
get: function(apiStuff) { // DO API CONNECT WITH .THEN HERE },
data: {}
}
service.get();
return service;
}
I would like one entry point towards the Api that handles all the $q stuff (my factory) I assumed I need to handle all the .then() stuff in my siteRoutes object, which is what I did.
Now, what happens in my app.run method is that I don't get the siteRoutes object with any data. So I recon I need to do a .then there as well?
But that made me question the entire design of putting all logic in a separate factory for the connection, because I basically like my app to just use the data and have my library deal with the async stuff (if you get what I am saying)...
Hope this is clear.
TL;DR -> How to make your services / factories handle async stuff without making your 'app' deal with it?
The templateUrl property can also be a function that takes the url parametes as input.
In the example below all routes will load a template with same name.
Eg. domain.com/#/blabla.html will load the view blabla.html from the server.
myApp.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/:templateName',
{
templateUrl: function (params) {
return params.templateName + ".html";
}
}
)
.otherwise({ redirectTo: '/main' });
}]);

Passing global data / user data to routeProvider in Angular + express application

This might seem like a silly question.. but how do I pass req.user.username (for example) to all pages / globally after the user signs in with passport. This question can apply to any data I would like accessible for all pages...
On the server side, I have the below which sends allows routeProvider to handle all client side routing.
app.get('*',
function(req, res) {
res.sendfile('./public/index.html')
// load the single view file (angular will handle the page changes on the front-end)
})
I'm not sure if the solution is specific to passport... express or involves both...
The client side routing is handled by something like:
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/', {templateUrl: 'views/home.html'})
.when('/login', {templateUrl: 'views/login.html'})
.when('/users', {templateUrl: 'views/users.html', controller: 'UserController'})
...
You have two options. Either
BAD - include that data in your compiled view that is initially served (./public/index.html) or
GOOD/COMMON - fetch the data you need inside something like an Angular controller which is in your view; eg.
$routeProvider.when('/', {
templateUrl: 'views/home.html',
controller: function($scope, myThings) {
//myThings is a service that async fetches some data from api
myThings().then(function(user) {
$scope.user = user;
});
}
});
This obviously means you are exposing data on an api endpoint, but how else would angular be fetching the bits it needs since you said this is a single-page app?
Services are the way to go if you want to share global data among controllers, directives and other services.
Depending upon the type of data, you can expose services that load data from the server or services which do not need to make remote call to load data (like some custom view settings).
For example if in case you want to get the current logged in user.
The first thing is to create a method on the server that return the current logged in user data in json format.
Then create something like a UserService or SessionService that call this server method to load currently loggedin user data.
Something like
angular.module('myApp').factory('SessionService',['$http',function($http) {
var service={};
service.getCurrentUser=function() {
return $http('/user');
};
return service;
}]);
Inject this service into your controllers to get the current user. You can optimize it to cache the user data.
If you want to use the data in routeProvider use the resolve property
.when('/users', {templateUrl: 'views/users.html', controller: 'UserController',
resolve: {
currentUser:function(SessionService) {
return SessionService.getCurrentUser();
}
}}})

Resources