I'm very new to angularjs and I want to establish a connection to my server and dynamically show the result to user. so far I've tried:
angular.module('myApp.controllers', []).controller('socketsController', function($scope) {
$scope.socket = {
client: null,
stomp: null
};
$scope.reconnect = function() {
setTimeout($scope.initSockets, 10000);
};
$scope.notify = function(message) {
$scope.result = message.body;
};
$scope.initSockets = function() {
$scope.socket.client = new SockJS('/resources');
$scope.socket.stomp = Stomp.over($scope.socket.client);
$scope.socket.stomp.connect({}, function() {
$scope.socket.stomp.subscribe('/user/topic/messages', $scope.notify);
});
$scope.socket.client.onclose = $scope.reconnect;
};
$scope.initSockets();
});
But when I use {{result}} nothing appears.
UPDATE
The server response is totally right with console.log(message.body).
I guess, the callback is not taking the scope properly. Try call $scope.$apply(); after you attach the message.body to result :
$scope.notify = function(message) {
$scope.result = message.body;
$scope.$apply();
};
$scope.$apply() triggers an angular digest cycle whcih will update all the bindings..
Call it inside a timeout function but inject $timeout first it will call the digest cycle and update the value.
$timeout(function(){
$scope.result = message.body;});
Related
Is there a way to add a timeout to an AngularJS $watch function?
For example, let's say I have the below AngularJS code that's watching a value, myName. When the value changes, the listener function runs. But if the value does not change within a certain period of time, I want it to do something else.
Specifically, in the code below, I would want $scope.nothingEnteredFlag to change from false to true. My html template be set up to reflect the state of that flag (e.g., using ng-show).
var app = angular.module("helloApp", []);
app.controller("helloCtrl", function($scope) {
$scope.nothingEnteredFlag=false;
$scope.$watch("myName", function (newValue, oldValue) {
if ($scope.myName.length < 5) {
$scope.message = "Short name!";
} else {
$scope.message = "Long name!";
}
});
});
See fiddle.
I've tried surrounding the $watch with $timeout, but can't seem to get that to work.
You can use angular timeout to achieve your desire result.
var timer;
var timerFunction = function() {
timer = $timeout(function() {
$scope.nothingEnteredFlag = true;
}, 5000);
};
This is will create the timer function
Your controller should like this
var app = angular.module("helloApp", []);
app.controller("helloCtrl", function($scope, $timeout) {
$scope.nothingEnteredFlag = false;
$scope.myName = "";
$scope.$watch("myName", function(newValue, oldValue) {
if ($scope.myName.length < 5) {
$scope.message = "Short name!";
} else {
$scope.message = "Long name!";
}
$scope.nothingEnteredFlag = false;
$timeout.cancel(timer);
timerFunction();
});
var timer;
var timerFunction = function() {
timer = $timeout(function() {
$scope.nothingEnteredFlag = true;
}, 5000);
};
timerFunction();
});
As you can see we have enabled timeout of the 5 seconds once user enters any text we cancel the timer and enable it again, this way we can prompt the user to enter if he hasn't wrote anything in five seconds.
Demo
I have used $timeout to call an angular JS service in every 5 seconds. But it leads to a page or cursor reload in my application. Can anyone assist me to stop the page reload?
var app = angular.module('myApp', ['ngAnimate']);
app.controller('MainCtrl', function($scope, $http, $timeout) {
var loadTime = 1000, //Load the data every second
errorCount = 0, //Counter for the server errors
loadPromise; //Pointer to the promise created by the Angular $timout service
var getData = function() {
//console.log('http://httpbin.org/delay/1?now=' + Date.now());
$http.get('http://httpbin.org/delay/1?now=' + Date.now())
.then(function(res) {
$scope.data = res.data.args;
errorCount = 0;
nextLoad();
})
.catch(function(res) {
$scope.data = 'Server error';
nextLoad(++errorCount * 2 * loadTime);
});
};
var cancelNextLoad = function() {
$timeout.cancel(loadPromise);
};
var nextLoad = function(mill) {
mill = mill || loadTime;
//Always make sure the last timeout is cleared before starting a new one
cancelNextLoad();
loadPromise = $timeout(getData, mill);
};
//Start polling the data from the server
getData();
//Always clear the timeout when the view is destroyed, otherwise it will keep polling and leak memory
$scope.$on('$destroy', function() {
cancelNextLoad();
});
$scope.data = 'Loading...';
});
Check to make sure loadPromise exists before cancelling it:
var cancelNextLoad = function() {
̶$̶t̶i̶m̶e̶o̶u̶t̶.̶c̶a̶n̶c̶e̶l̶(̶l̶o̶a̶d̶P̶r̶o̶m̶i̶s̶e̶)̶;̶
loadPromise && $timeout.cancel(loadPromise);
};
All:
I am pretty new to angular digest, right now, when I use Promise, in its then function, I have to use $scope.$digest() to make scope variable change takes effect on other place, like:
Here I use a Promise to emulate $http request, my confuse is in $scope.getdata, why I need to call $scope.$digest(), I thought $scope.disable should be watched by angular automatically.
var app = angular.module("vp", []);
app
.service("srh", function($http){
var busy = false;
this.get = function(url){
if(!busy){
busy = true;
var p = new Promise(function(res, rej){
$timeout(function(){
res("data is fetched");
}, 3000);
})
.then(function(data){
busy = false;
return data;
}, function(data){
busy = false;
return data;
});
return p;
}else {
return null;
}
}
})// end of service
.controller("main", function($scope, srh){
$scope.disable = false;
$scope.getdata = function(){
var dp = srh.get("");
if( dp ) {
$scope.disable = true;
dp.then(function(data){
console.log(data);
$scope.disable = false;
$scope.$digest()
})
}
}
})
Use $q angular promises which will internally handle all digest requirements.
Whenever you use events outside of angular core that modify scope you need to tell angular so it can update view
I have a View that is updated after 1 minute, I stop the timer after before leaving this view, and all is OK.
After returning to the current view the timer don't restart again.
This is the code of the controller of this view:
.controller('IndexCtrl', function($scope, $timeout, RestService) {
var updateN = 60*1000;
$scope.test = "View 1 - Update";
var update = function update() {
timer = $timeout(update, updateN);
/** make a http call to Rest API service and get data **/
RestService.getdata(function(data) {;
$scope.items = data.slice(0,2);
});
}();
/** Stop the timer before leave the view**/
$scope.$on('$ionicView.beforeLeave', function(){
$timeout.cancel(timer);
//alert("Before Leave");
});
/** Restart timer **/
$scope.$on('$ionicView.enter', function(){
$timeout(update, updateN);
//alert("Enter");
});
})
.controller('ViewCtrl2', function($scope) {
$scope.test = "View 2";
});
I resolve the problem,
There is not a problem with the cache, but with the function update that is not called after I re-enter on the page.
I move the update function inside the $ionicView.enter :
The corrected code is:
$scope.$on('$ionicView.beforeLeave', function(){
//updateN=12000000;
$timeout.cancel(timer);
//alert("Leave");
});
$scope.$on('$ionicView.enter', function(){
//updateN=12000000;
var update = function update() {
timer = $timeout(update, updateN);
RestService.getdata(function(data) {
//console.log(tani);
//$scope.items = data;
$scope.items = data.slice(0,2);
});
}();
});
When you go back to current view, it comes from the cache, so the controller does not work again. You can disable caching in the config section of your app by adding this line of code :
$ionicConfigProvider.views.maxCache(0);
or you can disable cache on a specific view in the routing part by addding
cache : false property.
More information here and here
In your code your controller function does not call on change of view. call $timeout function outside of var update function. Each time view loads it call its controller and call anonymous or self executing functions in their scope.
.controller('IndexCtrl', function($scope, $timeout, RestService) {
var updateN = 60 * 1000;
$scope.test = "View 1 - Update";
var update = function update() {
var timer = $timeout(update, updateN);
/** make a http call to Rest API service and get data **/
RestService.getdata(function(data) {;
$scope.items = data.slice(0, 2);
});
}();
/** Stop the timer before leave the view**/
$scope.$on('$ionicView.beforeLeave', function() {
$timeout.cancel(timer);
//alert("Before Leave");
});
/** Restart timer **/
$scope.$on('$ionicView.enter', function() {
timer()
});
})
.controller('ViewCtrl2', function($scope) {
$scope.test = "View 2";
});
$watch() is not catching return sseHandler.result.cpuResult.timestamp after the first iteration. I'm not sure why, because I verified the datestamps are changing. Also, after the first iteration....if I click on the view repeatedly, the scope variables and view update with the new information...so it's like $watch does work...but only if I click on the view manually to make it work.
'use strict';
angular.module('monitorApp')
.controller('homeCtrl', function($scope, $location, $document) {
console.log("s");
});
angular.module('monitorApp')
.controller('cpuCtrl', ['$scope', 'sseHandler', function($scope, sseHandler) {
$scope.sseHandler = sseHandler;
$scope.avaiable = "";
$scope.apiTimeStamp = sseHandler.result.cpuResult.timestamp;
$scope.infoReceived = "";
$scope.last15 = "";
$scope.last5 = "";
$scope.lastMinute = "";
var cpuUpdate = function (result) {
$scope.available = result.cpuResult.avaiable;
$scope.apiTimeStamp = result.cpuResult.timestamp;
$scope.infoReceived = new Date();
$scope.last15 = result.cpuResult.metrics['15m'].data
$scope.last5 = result.cpuResult.metrics['5m'].data
$scope.lastMinute = result.cpuResult.metrics['1m'].data
}
$scope.$watch(function () {
console.log("being caught");
return sseHandler.result.cpuResult.timestamp},
function(){
console.log("sss");
cpuUpdate(sseHandler.result);
});
}]);
angular.module('monitorApp')
.controller('filesystemsCtrl', function($scope, $location, $document) {
console.log("s");
});
angular.module('monitorApp')
.controller('httpPortCtrl', function($scope, $location, $document) {
console.log("s");
});
angular.module('monitorApp')
.factory('sseHandler', function ($timeout) {
var source = new EventSource('/subscribe');
var sseHandler = {};
sseHandler.result = { "cpuResult" : { timestamp : '1'} };
source.addEventListener('message', function(e) {
result = JSON.parse(e.data);
event = Object.keys(result)[0];
switch(event) {
case "cpuResult":
sseHandler.result = result;
console.log(sseHandler.result.cpuResult.timestamp);
break;
}
});
return sseHandler;
});
The changes in sseHandler.result.cpuResult.timestamp happen otuside of the Angular context (in the asynchronously executed event-listener callback), so Angular does not know about the changes.
You need to manually trigger a $digest loop, by calling $rootScope.$apply():
.factory('sseHandler', function ($rootScope, $timeout) {
...
source.addEventListener('message', function (e) {
$rootScope.$apply(function () {
// Put all code in here, so Angular can also handle exceptions
// as if they happened inside the Angular context.
...
});
}
...
The reason your random clicking around the app made it work, is because you probably triggered some other action (e.g. changed a model, triggered and ngClick event etc) which in turn triggered a $digest cycle.
Your message event in the EventListener does not start a new digest cycle. In your sseHandler try:
$timeout(function () {sseHandler.result = result;});