I have successfully implemented the chatroom in angularjs using node + socket.io
But hope you guys can help me, I am stuck in a situation where I listen to the socket on the clientside
socket.on('new message', function(data){
$scope.messages.push(data);//then run ng-repeat in the template
});
Problem is,
1) if I put the above method inside the controller, the above get reinitialized(multiple listener binds) whenever I open that page again and again(We have multi page app)
or
2) if I put the above method (as the docs says) at a global place I lose the scope of the controller so I cant bind the latest model to the template
Any help??
You could try disconnecting the socket when the $scope is destroyed...
$scope.$on('$destroy', function(){
socket.disconnect();
});
But I like the service based approach...
var SocketService = function(){
var _messages = [];
socket = connectMeToSomeSocket();
socket.on('new message', function(data){
_messages.push(data);
});
Object.defineProperty(this, 'messages', {
get: function(){
return _messages;
}
})
};
Then inject the socketService in your controllers...
angular.controller('SomeCtrl',['$scope', 'socketService', function($scope, socketService){
$scope.socket = socketService;
}])
And use socket.messages in your template...
<li ng-repeat="msg in socket.messages">
If you don't like giving your templates access to the socketService or you don't like Object.defineProperty, then you can add a bindScope method your service...
this.bindScope = function($scope){
var stopWatching = $scope.$watchCollection(function(){ return _messages;}, function(){
$scope.messages = _messages;
});
// may not be necessary as `Scope` prob cleans up after itself
$scope.$on('$destroy', stopWatching);
};
And use it in your controllers...
socketService.bindScope($scope);
Related
I am setting up a registration page where after the user submits, they are emailed an activation link. When they click on the activation link in the email, I want the account get activated on the server and then I want my angularJS controller to catch the response from the server. I got the activation part working, but I am unsure on how to catch the response from my server since the call to the server did not originate from an angular view, but from an email.
Try out the below code:
Html:
<div ng-controller="activationCtrl as vm">
<h3>{{vm.activationSuccessMessage}}</h3>
<div>
Controller:
(function(){
'use strict';
angular
.module('your moduleName',[])
.controller('activationCtrl',activationCtrl);
activationCtrl.$inject = ['$http'];
function activationCtrl($http)
{
var vm = this;
vm.activationSuccessMessage = "";
var param = {}; //if any
$http.post('api/Users/Activate', param)
.then(function(response)
{
//Success - Do what you need to do.
vm.activationSuccessMessage = "Activation Successful";
}, function(response){
//Fail - Handle it.
vm.activationSuccessMessage = "Activation failed";
});
}
})();
Inside your activation page controller on load.
//Get activation value however you want to do that.
var activationToken = ValueFromURLSomehow.
//Can keep it simple, with a $http request directly in controller, or use a service.
$http.get('api/Account/Activate',{params{token:activationToken}}).then(function(response){
//Success - Do what you need to do.
}, function(response){
//Fail - Handle it.
});
How do I run a function when socket.io updates a variable. It seems that $watch does not work or purhaps I am doing something wrong.
$http.get('/api/availabilitys/' + myId).then(response => {
$scope.availability = response.data;
socket.syncUpdates('availability', $scope.availability);
});
$scope.$watch('availability', function() {
console.log('test'); //This is not printing on update
angular.forEach(self.location, function (loc){
loc.availability = $scope.availability.filter(function (a){
return a.loc === loc._id;
});
});
});
The functions have to be in the same controller / scope because of encapsulation. The $scope of angular does not know if socket.io updates a variable, use a socket.on() listener to trigger the angular $watch
Try the code in this thread Socket.IO message doesn't update Angular variable
Instead of $watch in angular you can use socket.on() of socket.io
var container = angular.module("AdminApp", []);
container.controller("StatsController", function($scope) {
var socket = io.connect();
socket.on('message', function (msg) {
console.log(msg);
$scope.$apply(function() { $scope.frontEnd = msg; });
});
});
See (the posts at the end of) this thread
How do I use $scope.$watch and $scope.$apply in AngularJS?
I am a newbie at angularjs and i am creating a web application to earn experience and practice. The problem i have is that $scope.$emit does not seem to be working, i am looking for ways to contact functions between controllers and so far i have found on the internet that $scope.emit and $scope.on seems to fit for this kind of task, if there is another way, i would like to know, anyways the code are written like this:
loginController.js
(function(angular)
{
var app = angular.module('Organizer');
// inject $scope to the controller in order to try $scope.$emit
app.controller('login', function($http, $scope)
{
// i define the scope like this so i can access inside other functions
var scope = this;
scope.processing = false;
scope.message = null;
scope.submit = function()
{
scope.processing = true;
// store data for post
var post = {
username: scope.username,
password: scope.password
};
// send post data
$http.post('api/default/login', post).
success(function(data, status)
{
// hide processing feedback and show the result
scope.processing = false;
scope.message = data.message;
}).
error(function(data, status)
{
scope.processing = false;
});
};
// Function i use to emit
this.closeDialog = function()
{
$scope.$emit('closeDialog');
};
});
})(angular);
siteController.js
(function(angular)
{
var app = angular.module('Organizer');
app.controller('site', function($mdDialog, $scope)
{
this.menu = ['test1', 'test2'];
this.dialog = function()
{
$mdDialog.show({
templateUrl: 'site/login',
});
};
// this does not seem to be working
$scope.$on('closeDialog', function(event)
{
console.log('close dialog');
});
});
})(angular);
Note: i am using angular material and you can see i am showing a dialog which is a login, the login has its controller (i wanted it to use the same site controller, but i don't know how) and this dialog has a button which calls the function closeDialog() in loginControler and should close the dialog, but for now for testing reasons i am just logging if it's calling the event
The $emit function propagate an event only to the scopes parents.
The $broadcast function propagate an event to the scopes childs.
So what you need depends on how the controllers are use it...
If you want an event to reach all the app you have to use the $rootScope:
$rootScope.$broadcast('myEvent');
Here you have the doc of the scope, include $emit and $broadcast
You could not emit or broadcast in dialog controller because dialog in angular material has isolated scope. Because of that, when you emit or broadcast an event, it does not go anywhere. The $emit and $broadcast only works when you have scope hierarchy. $emit propagate event up the hierarchy and $broadcast propagate event down the hierarchy.
I am using socket.io to enable chat in my app and i am using a service SocketService to perform all the socket stuff. When a message came then i want to trigger a function of a controller from the service SocketService to make some changes in the UI.
So i want to know that how can i access the function of a controller from the service.
Sample Code:
.service('SocketService', function ($http,$rootScope,$q) {
this.connect = function(){
var socket = io();
socket.on('connect',function(){
// Call a function named 'someFunction' in controller 'ChatController'
});
}
});
This is the sample code for service.
Now the code for controller
.controller('ChatController',function('SocketService',$scope){
$scope.someFunction = function(){
// Some Code Here
}
});
You could achieve this by using angular events $broadcast or $emit.
In your case $broadcast would be helpful,
You need to broadcast your event in $rootscope that can be listen by all the child scopes which has $on with same event name.
CODE
.service('SocketService', function($http, $rootScope, $q) {
this.connect = function() {
var socket = io();
socket.on('connect', function() {
// Call a function named 'someFunction' in controller 'ChatController'
$rootScope.$broadcast('eventFired', {
data: 'something'
});
});
}
});
.controller('ChatController', function('SocketService', $scope) {
$scope.someFunction = function() {
// Some Code Here
}
$scope.$on('eventFired', function(event, data) {
$scope.someFunction();
})
});
Hope this could help you, Thanks.
I know this is an old question, but I have another option. I have a personal bias against $broadcast - it just doesn't feel very 'angularish', I prefer making explicit calls in my code.
So instead of broadcasting to the controller and triggering another digest cycle, I prefer to have the controller register itself to the service, as below. Just be careful not to introduce any circular dependencies if the controller makes use of the same service. This works best with the controllerAs syntax, so that the calling service does not need to care about $scope.
Yes, this is more code than $broadcast, but it does give the service total access to the entire controller - all of it's methods and properties.
.service('SocketService', function ($http,$rootScope,$q) {
var _this = this;
this.chatController = null;
this.registerCtrlr = function (ctrlr) {
_this.chatController = ctrlr;
};
this.unRegisterCtrlr = function () {
_this.chatController = null;
};
this.connect = function(){
var socket = io();
socket.on('connect',function(){
// Call chatController.someFunction if chatController exists
if (_this.chatController) {
_this.chatController.someFunction();
}
});
};
});
.controller('ChatController',['SocketService', '$scope', function(SocketService, $scope){
SocketService.registerCtrlr(this);
//-- make sure controller unregisters itself when destroyed - need $scope for this
$scope.$on('$destroy', function () {
SocketService.unRegisterCtrlr();
});
this.someFunction = function(){
// Some Code Here
}
}]);
I realize this post is old but I'd like to give my two cents after dealing with Angular JS for several years. I personally would reconsider this approach. Ideally with AngularJS you'd modify your controller/directive to facilitate transferring data to the view model and ultimately bind an HTML template to what I call "the user friendly" view model. This view model should simply reflect what you want the user to see and when in general. Using this method the moment connect event happens your view model which should be bound to the service's data will reflect changes to the data the moment the data arrives.
I am trying to call a method of second controller in first controller by using scope variable. This is a method in my first controller:
$scope.initRestId = function(){
var catapp = document.getElementById('SecondApp');
var catscope = angular.element(catapp).scope();
catscope.rest_id = $scope.user.username;
catscope.getMainCategories();
};
I am able to set the value of rest_id but I cannot call getMainCategories for some reason. The console shows this error:
TypeError: Object # has no method 'getMainCategories'
Is there a way to call the above method?
Edit:
I used the following approach to load two apps at the same time;
angular.bootstrap(document.getElementById('firstAppID'), ['firstApp']);
angular.bootstrap(document.getElementById('secondAppID'), ['secondApp']);
I could definitely use a service here, but I wanted to know if there are any other options to do the same!
The best approach for you to communicate between the two controllers is to use events.
Scope Documentation
In this check out $on, $broadcast and $emit.
In general use case the usage of angular.element(catapp).scope() was designed for use outside the angular controllers, like within jquery events.
Ideally in your usage you would write an event in controller 1 as:
$scope.$on("myEvent", function (event, args) {
$scope.rest_id = args.username;
$scope.getMainCategories();
});
And in the second controller you'd just do
$scope.initRestId = function(){
$scope.$broadcast("myEvent", {username: $scope.user.username });
};
Edit: Realised it was communication between two modules
Can you try including the firstApp module as a dependency to the secondApp where you declare the angular.module. That way you can communicate to the other app.
Here is good Demo in Fiddle how to use shared service in directive and other controllers through $scope.$on
HTML
<div ng-controller="ControllerZero">
<input ng-model="message" >
<button ng-click="handleClick(message);">BROADCAST</button>
</div>
<div ng-controller="ControllerOne">
<input ng-model="message" >
</div>
<div ng-controller="ControllerTwo">
<input ng-model="message" >
</div>
<my-component ng-model="message"></my-component>
JS
var myModule = angular.module('myModule', []);
myModule.factory('mySharedService', function($rootScope) {
var sharedService = {};
sharedService.message = '';
sharedService.prepForBroadcast = function(msg) {
this.message = msg;
this.broadcastItem();
};
sharedService.broadcastItem = function() {
$rootScope.$broadcast('handleBroadcast');
};
return sharedService;
});
By the same way we can use shared service in directive. We can implement controller section into directive and use $scope.$on
myModule.directive('myComponent', function(mySharedService) {
return {
restrict: 'E',
controller: function($scope, $attrs, mySharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'Directive: ' + mySharedService.message;
});
},
replace: true,
template: '<input>'
};
});
And here three our controllers where ControllerZero used as trigger to invoke prepForBroadcast
function ControllerZero($scope, sharedService) {
$scope.handleClick = function(msg) {
sharedService.prepForBroadcast(msg);
};
$scope.$on('handleBroadcast', function() {
$scope.message = sharedService.message;
});
}
function ControllerOne($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'ONE: ' + sharedService.message;
});
}
function ControllerTwo($scope, sharedService) {
$scope.$on('handleBroadcast', function() {
$scope.message = 'TWO: ' + sharedService.message;
});
}
The ControllerOne and ControllerTwo listen message change by using $scope.$on handler.
Each controller has it's own scope(s) so that's causing your issue.
Having two controllers that want access to the same data is a classic sign that you want a service. The angular team recommends thin controllers that are just glue between views and services. And specifically- "services should hold shared state across controllers".
Happily, there's a nice 15-minute video describing exactly this (controller communication via services): video
One of the original author's of Angular, Misko Hevery, discusses this recommendation (of using services in this situation) in his talk entitled Angular Best Practices (skip to 28:08 for this topic, although I very highly recommended watching the whole talk).
You can use events, but they are designed just for communication between two parties that want to be decoupled. In the above video, Misko notes how they can make your app more fragile. "Most of the time injecting services and doing direct communication is preferred and more robust". (Check out the above link starting at 53:37 to hear him talk about this)