AngularJS - Controller variable not updating from a service function - javascript

I have this variable that is being controlled by the Factory and to update the Controller but it's not happening.
Here is what I have:
var app = angular.module('plunker', []);
app.controller('AppController', function($scope, AppFactory) {
var vm = this;
$scope.serverStatus = AppFactory.getStatus();
});
app.factory('AppFactory', function($timeout) {
var AppFactory = {};
var vm = this;
vm.serverStatus = true;
// Execute after 2 seconds of page start
$timeout(function() {
AppFactory.setStatus(false);
}, 2000);
AppFactory.setStatus = function(status) {
console.log('Server set to ' + status);
vm.serverStatus = status;
// Getting server status = false
AppFactory.getStatus();
};
AppFactory.getStatus = function() {
console.log('Getting server status: ' + vm.serverStatus);
return vm.serverStatus;
};
return AppFactory;
});
LIVE PLUNKER DEMO: https://plnkr.co/edit/62xGw7Klvbywp9TODWF4?p=preview
Do you think Directives would work better with 2-way-communication between a factory and controller?

Check this edited the plunkr https://plnkr.co/edit/z6tdr5?p=preview
var app = angular.module('plunker', []);
app.controller('AppController', function($scope,$timeout, AppFactory) {
var vm = this;
$timeout(function() {
AppFactory.setStatus(false);
$scope.serverStatus = AppFactory.getStatus();
}, 2000);
$scope.serverStatus = AppFactory.getStatus();
});
app.factory('AppFactory', function($timeout) {
var AppFactory = {};
var serverStatus = true;
// Execute after 2 seconds of page start
return {
getStatus: function () {
//console.log('Getting server status: ' + vm.serverStatus);
return serverStatus;
},
setStatus : function(status) {
var vm = this;
console.log('Server set to ' + status);
serverStatus = status;
// Getting server status = false
vm.getStatus();
}
};
});

Here's a solution that uses events, e.g.:
app.controller('AppController', function($scope, AppFactory) {
var vm = this;
$scope.$on('messageOne', function(event, data){
console.log(data);
$scope.serverStatus = data;
$scope.$apply(); //I think $apply() is not needed here!
});
$scope.serverStatus = AppFactory.getStatus();
});
app.factory('AppFactory', function($timeout, $rootScope) {
var AppFactory = {};
var vm = this;
vm.serverStatus = true;
// Execute after 2 seconds of page start
$timeout(function() {
AppFactory.setStatus(false);
}, 2000);
AppFactory.setStatus = function(status) {
console.log('Server set to ' + status);
vm.serverStatus = status;
// Getting server status = false
//AppFactory.getStatus();
$rootScope.$broadcast('messageOne', status);
};
AppFactory.getStatus = function() {
console.log('Getting server status: ' + vm.serverStatus);
return vm.serverStatus;
};
return AppFactory;
});
https://plnkr.co/edit/pARMnE3Wl0OeJezKuvLT?p=info

Related

I can't get data from controller AngularJS

I want to make chat app. So when I send message you can hear pop. So i have two directives, one for my messages and other for simulation. Simulation is working just one time and then stop. I cant get objects from $scope.data to directive myDirective. I really tried almost everything. Is there any solution?
var app = angular.module('myApp', ['ui.router']);
app.config(function($stateProvider, $urlRouterProvider,$locationProvider) {
//angular-ui-router
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home',{
url:'/',
views:{
'':{
templateUrl:'../ChatApp/views/chatapp.html'
}
}
})
.state('login',{
url:'/login',
templateUrl:'../ChatApp/views/login.html',
controller: 'LoginController'
});
});
app.controller('MessageController', function($scope,$filter, $http,$state,$timeout,$compile,$interval) {
$scope.randomNumber = Math.floor(Math.random()*(2-0+1)+0);
$scope.data = [];
$scope.name = localStorage.getItem('Name');
console.log(localStorage);
console.log(localStorage.getItem('Name'));
$scope.sendedMessage = "";
$scope.data1 = [$scope.name,$scope.sendedMessage,$scope.date];
//geting data from json
$scope.getData = function () {
$http.get('../ChatApp/data/data.json')
.then(function(response) {
$scope.data =response.data.messages;
console.log($scope.data);
});
};
//logout button
$scope.logout = function () {
localStorage.clear();
console.log(localStorage);
};
//function for rocket button
$scope.sendMessage = function (message) {
$scope.date = new Date();
$scope.date = $filter('date')(new Date(), 'HH:mm:ss');
$scope.message = null;
$scope.sendedMessage = message;
var audio = new Audio('../ChatApp/sounds/videoplayback');
audio.play();
};
//simple autentification
$scope.login = function () {
if(localStorage.getItem('Name') == null){
$state.go('login');
}
};
(function() {
$scope.getData();
$scope.login();
})();
});
app.controller('LoginController', function($scope, $state) {
//simple login
$scope.login = function (name) {
if(name == ""){
alert('Write your name! ');
}
else
{
localStorage.setItem('Name', $scope.name);
$state.go('home');
}
}
});
app.$inject = ['$scope'];
//When you click button rocket, you are adding this to html
app.directive("boxCreator", function($compile){
return{
restrict: 'A',
link: function(scope , element){
if(scope.sendedMessage != undefined && scope.sendedMessage!=null){
element.bind("click", function(e){
var childNode = $compile('<li class="self"><div class="msg"><div class="user">{{:: name}}</div><p>{{:: sendedMessage}}</p><time>{{:: date}}</time></div></li>')(scope);
angular.element( document.querySelector('#chat')).append(childNode);
});
}
//doStuff literally does nothing
scope.doStuff = function(){
alert('hey');
}
}
}
});
app.directive('myDirective', function() {
return {
compile: function(element, attr,$compile) {
var newElement = angular.element('<li class="other"><div class="msg"><div class="user">{{data[randomNumber].name}}</div><p>{{data[randomNumber].message}}</p><time>{{data[randomNumber].date}}</time></div></li>');
element.append(newElement);
var audio = new Audio('../ChatApp/sounds/videoplayback');
audio.play();
return function(scope, element, attr) {
setInterval(function () {
scope.$watch('data', function (data) {
console.log("fff", scope.data);
});
var newElement = angular.element('<li class="other"><div class="msg"><div class="user">{{name}}</div><p>{{scope.data[0].message}}</p><time>{{data[0].date}}</time></div></li>');
element.append(newElement);
var audio = new Audio('../ChatApp/sounds/videoplayback');
audio.play();
},5000);
}
}
}
});
});

How to identify the specific Interval in angular js to destroy

Below directive is used for getting data continuously from server with default interval of 5 sec
app.directive('livestatDataGrid', livestatDataGrid);
function livestatDataGrid($http, $interval, $window) {
return {
restrict: 'E',
templateUrl: 'directives/livestatDataGrid/livestat-datagrid.html',
scope: {
viewName: '=viewName',
gridOptions: '=',
deleteItem: '=deleteItem'
},
link: function($scope) {
var defaultSec = 5;
$scope.timerObj = {};
$scope.timerObj.selectedItem = defaultSec;
$scope.timerObj.isDisplay = true;
var timer = null;
$scope.copyVal = defaultSec;
$scope.getViewData = function(viewName) {
console.log('Came here !! Directive ', viewName);
$http.get('json/' + viewName + '.json').then(function success(response) {
$scope.metricHeader = response.data.metricHeader;
$scope.stats = response.data.stats;
}, function error(error) {
console.log("Eooor ", error)
});
};
$scope.getViewData($scope.viewName);
// Timer code
$scope.editTimer = function() {
$scope.timerObj.isDisplay = false;
};
$scope.setTimer = function() {
if ($scope.timerObj.selectedItem && isNaN($scope.timerObj.selectedItem)) {
$scope.timerObj.selectedItem = defaultSec;
}
if ($scope.copyVal != "" && $scope.copyVal == $scope.timerObj.selectedItem) {
$scope.timerObj.isDisplay = true;
return;
}
$scope.StopTimer();
$scope.copyVal = angular.copy($scope.timerObj.selectedItem);
$scope.timerObj.isDisplay = true;
$scope.startTimer();
};
// starting the timer here once directive is initialized.Using `$interval`
$scope.startTimer = function() {
console.log("in timer " + $scope.timerObj.selectedItem);
if ($scope.timerObj.selectedItem && $scope.viewName) {
//time interval for sending request to server
timer = $interval(function() {
if ($scope.viewName)
$scope.getViewData($scope.viewName);
$interval.cancel(this);
console.log(timer)
}, $scope.timerObj.selectedItem * 1000);
}
};
$scope.startTimer();
// Once the job is done, destroy the timer using `$interval.cancel`.
$scope.StopTimer = function() {
if (angular.isDefined(timer)) {
console.log("in stop timer");
$interval.cancel(timer);
}
};
//timer code ends her
}
}
}
Not clear what do you intend to ask/discuss. Is this a self answer question?

How to change controller variable in service angular?

This's my service. I've got variable isPolygonDrawingEnded there.
angular
.module('gillie.clients.app')
.service('mapDrawingService', mapDrawingService);
mapDrawingService.$inject = ['$ngBootbox', '$translate', 'fitPolygonService', 'mapCommonService'];
function mapDrawingService($ngBootbox, $translate, fitPolygonService, mapCommonService) {
var self = this;
var map;
var polygonFeatures;
var stopDrawingAction;
var isPolygonDrawingEnded = false;
var isDrawingMode = true;
self.startNewPolygonDrawing = function (afterDrowed) {
fitPolygonService.suggestPoint(isDrawingMode);
var modify = createModifyInteractionToPolygonFeatures();
var draw = createDrawInteraction();
attachDrawEvents(draw, afterDrowed);
map.addInteraction(draw);
stopDrawingAction = function() {
map.removeInteraction(draw);
map.removeInteraction(modify);
};
};
var attachDrawEvents = function (draw, afterDrowed) {
draw.on('drawend', function (e) {
e.preventDefault();
afterDrowed();
isPolygonDrawingEnded = true;
if (fitPolygonService.isPolygonsExist()) {
var geometry = e.feature.getGeometry();
e.feature.setGeometry(fitPolygonService.fitPolygon(geometry));
}
});
}
This is my controller. Here I need to change $scope.showCountButton when mapDrawingService.isPolygonDrawingEnded == true. So how can I know that value in service is changed ?
angular
.module('gillie.clients.app')
.controller('regionController', regionController);
regionController.$inject = ['$q', '$filter', '$scope', '$ngBootbox', '$translate', 'regionService', 'mapService', 'mapDrawingService', 'modalService', 'regionCommonService', 'alertService', 'nominatimCommonService', 'populationService', 'provinceCommonService', 'provinceService', 'rx'];
function regionController($q, $filter, $scope, $ngBootbox, $translate, regionService, mapService, mapDrawingService, modalService, regionCommonService, alertService, nominatimCommonService, populationService, provinceCommonService, provinceService, rx) {
$scope.showCountButton = false;
$scope.startRegionSelection = function (region) {
$scope.isSavingAvailable = true;
$scope.showCountButton = false;
if (currentEditingRegion && region && currentEditingRegion.Id === region.Id) {
return;
}
if(region)
mapService.clearRegion(region);
if (currentEditingRegion && !region) {
mapDrawingService.continuePolygonDrawing();
return;
}
if (region) {
mapDrawingService.stopPolygonDrawing();
mapDrawingService.clearMap();
currentEditingRegion = region;
mapDrawingService.startExistPolygonDrawing(region.Name, region.CoordinatesJson);
return;
}
mapDrawingService.startNewPolygonDrawing(function () {
$scope.showCountButton = true
});
};
When I use mapDrawingService.startNewPolygonDrawing function - the value of showCountButton changes but view doesn't. $scope.$apply produces this exception:
[$rootScope:inprog] $digest already in progress
When you are passing the callback function it is no longer in the scope of the controller which is why the view is not updating. You can use promises instead since you are asynchronous draw event.
Service
mapDrawingService.$inject = ['$q', '$ngBootbox', '$translate', 'fitPolygonService', 'mapCommonService'];
function mapDrawingService($q, $ngBootbox, $translate, fitPolygonService, mapCommonService) {
var self = this;
var map;
var polygonFeatures;
var stopDrawingAction;
var isPolygonDrawingEnded = false;
var isDrawingMode = true;
self.startNewPolygonDrawing = function() {
fitPolygonService.suggestPoint(isDrawingMode);
var modify = createModifyInteractionToPolygonFeatures();
var draw = createDrawInteraction();
var promiseObj = attachDrawEvents(draw);
map.addInteraction(draw);
stopDrawingAction = function() {
map.removeInteraction(draw);
map.removeInteraction(modify);
};
return promiseObj;
};
var attachDrawEvents = function(draw) {
return $q(function(resolve, reject) {
draw.on('drawend', function(e) {
e.preventDefault();
if (fitPolygonService.isPolygonsExist()) {
var geometry = e.feature.getGeometry();
e.feature.setGeometry(fitPolygonService.fitPolygon(geometry));
}
resolve();
});
});
}
Controller
mapDrawingService.startNewPolygonDrawing().then(function() {
$scope.showCountButton = true
});

angular websocket factory

I have websocket service that works great when page is loaded. However, if connection is lost, and the service is trying to reconnect I am getting an error: "Uncaught ReferenceError: Service is not defined". Once I manually refresh page, service is working again. How can I reconnect without page refreshing? The app must reestablish that connection without any user involvement. This is my first angular app, so I am still in the process of learning the framework. Thank you.
angular.module('proApp').factory('webSocketService',
['$q', '$rootScope', function($q, $rootScope) {
var timeout = 2000;
var clearTimer = -1;
var port = '8081';
var server = '127.0.0.1';
var socket;
var host;
var Service = {};
function getSocketState() {
return (socket != null) ? socket.readyState : 0;
}
function onMessage(e) {
//console.log(e.data);
Service.message = JSON.parse(e.data);
$rootScope.$apply(function() {
Service.send();
});
}
//allows data to be used in controller
Service.send = function() {
$rootScope.$broadcast('broadcast');
};
function onError() {
clearInterval(clearTimer);
socket.onclose = function() {
};
clearTimer = setInterval("Service.getData()", timeout);
}
function onClose() {
clearInterval(clearTimer);
clearTimer = setInterval("Service.getData()", timeout);
}
function onOpen() {
clearInterval(clearTimer);
console.log("open" + getSocketState());
}
Service.getData = function() {
if ("WebSocket" in window) {
if (getSocketState() === 1) {
socket.onopen = onOpen;
clearInterval(clearTimer);
console.log(getSocketState());
} else {
try {
host = "ws://" + server + ":" + port + '';
socket = new WebSocket(host);
socket.onopen = onOpen;
socket.onmessage = function(e) {
onMessage(e);
};
socket.onerror = onError;
socket.onclose = onClose;
} catch (exeption) {
console.log(exeption);
}
}
}
};
// Public API here
return Service;
}]);
You have to change the use of setInterval to be like this:
clearTimer = setInterval(function () {
Service.getData();
}, timeout);
or just this:
clearTimer = setInterval(Service.getData, timeout);
This is the code I use, it can:
reconnect if connection is lost.
queue items while disconnected and send them on re-connection.
regular subscribing using the "listen" method.
"listenOnce" for an event once with a promise, after that the subscription is removed. Ideal for request/response using a correlationId
$rootScope.websocketAvailable indicates when the connection is available.
$rootScope.queuedMessages indicates when there are pending messages to be sent.
It is still part of a project in development, but I guess you can get the idea:
.service('$connection', ["$q", "$timeout", "websocketUrl", "$rootScope", function ($q, $timeout, websocketUrl, $rootScope) {
var connection = function () {
var me = {};
var listeners = [];
var oneListeners = [];
me.isConnected = false;
oneListeners.removeOne = function (listener) {
var index = oneListeners.indexOf(listener);
if(index!=-1)
oneListeners.splice(index, 1);
};
var correlationId = 0;
me.nextCorrelationId = function () {
return correlationId++;
};
$rootScope.queuedMessages = [];
me.listen = function (predicate, handler) {
listeners.push({ p: predicate, h: handler });
};
me.listenOnce = function (predicate, timeout) {
var deferred = $q.defer();
deferred.done = false;
var listener = { d: deferred, p: predicate };
oneListeners.push(listener);
if (timeout) {
$timeout(function () {
if (!deferred.done)
deferred.reject('timeout');
oneListeners.removeOne(listener);
}, timeout);
}
var promise = deferred.promise;
promise.then(function (data) {
deferred.done = true;
});
return promise;
};
var onopen = function () {
console.log('onopen');
$rootScope.websocketAvailable = true;
me.isConnected = true;
$rootScope.$$phase || $rootScope.$apply();
if ($rootScope.queuedMessages) {
for (var i = 0; i < $rootScope.queuedMessages.length; i++) {
ws.send(JSON.stringify($rootScope.queuedMessages[i]));
}
$rootScope.queuedMessages = null;
$rootScope.$$phase || $rootScope.$apply();
}
};
var onclose = function () {
console.log('onclose');
me.isConnected = false;
$rootScope.websocketAvailable = false;
$rootScope.$$phase || $rootScope.$apply();
$rootScope.queuedMessages = $rootScope.queuedMessages || [];
setTimeout(function () {
ws = connect();
}, 5000);
};
var onmessage = function (msg) {
console.log('onmessage');
var obj = JSON.parse(msg.data);
for (var i = 0; i < listeners.length; i++) {
var listener = listeners[i];
if (listener.p(obj))
listener.h(obj);
}
var remove = [];
for (var i = 0; i < oneListeners.length; i++) {
var listener = oneListeners[i];
if (listener.p(obj)) {
var o = obj;
listener.d.resolve(o);
remove.push(listener);
}
}
for (var i = 0; i < remove.length; i++) {
oneListeners.removeOne(remove[i]);
}
};
var onerror = function () {
console.log('onerror');
};
me.send = function (obj) {
if ($rootScope.queuedMessages)
$rootScope.queuedMessages.push(obj);
else
ws.send(JSON.stringify(obj));
}
var setHandlers = function (w) {
w.onopen = onopen;
w.onclose = onclose;
w.onmessage = onmessage;
w.onerror = onerror;
};
var connect = function () {
console.log('connecting...');
var w = new WebSocket(websocketUrl);
setHandlers(w);
return w;
}
var ws = connect();
return me;
};
return connection();
}])

getting TypeError: Cannot call method 'then' of undefined in angularjs controller

I keep getting the error - TypeError: Cannot call method 'then' of undefined when i call a method (database.readOnlyStock()) in my factory. also what i find a little ackward is that the function then does run accordingly because i see the console.log messages.
here is the factory code:
stocks.factory('database', ['$resource', '$http', '$q', function($resource, $http, $q) {
var dataMethods = {
createConnection: function() {
/*moved to onload below */
},
addStock: function(stock, nameofcompany) {
var _this = this;
console.log("About to add stock");
//Get a transaction
//default for OS list is all, default for type is read
var transaction = this.db.transaction(["portfolio"],"readwrite");
//Ask for the objectStore
var store = transaction.objectStore("portfolio");
//Define a person
var stockTemplate = {
company_name: nameofcompany,
name:stock,
created:new Date()
}
//Perform the add
var request = store.add(stockTemplate);
request.onerror = function(e) {
console.log("failed to add stock to portflio",e.target.error.name);
//some type of error handler
}
request.onsuccess = function(e) {
console.log("successfully added stock to portfolio");
//console.log(store);
//_this.readOnlyStock();
}
},
readOnlyStock: function() {
var deferred = $q.defer();
var transaction = this.db.transaction(["portfolio"],"readonly");
var store = transaction.objectStore("portfolio");
// var cursorRequest = store.openCursor();
var arrayOfStocks = [];
var keyRange = IDBKeyRange.lowerBound(0);
var cursorRequest = store.openCursor(keyRange);
cursorRequest.onsuccess = function(e) {
var cursor = e.target.result;
if(cursor){
arrayOfStocks.push(cursor.value);
cursor.continue();
}
else{
console.log(arrayOfStocks);
console.log('done!');
deferred.resolve(arrayOfStocks);
return deferred.promise;
//return arrayOfStocks;
}
}
cursorRequest.onerror = function(){
console.log('could not fetch data');
}
},
deleteStock: function() {}
}
//return dataMethods;
if (window.indexedDB) {
console.log('indexeddb is supported');
var openRequest = indexedDB.open("users_stocks", 4);
openRequest.onupgradeneeded = function(e) {
var thisDB = e.target.result;
if (!thisDB.objectStoreNames.contains("portfolio")) {
console.log('created object store');
thisDB.createObjectStore("portfolio", {
autoIncrement: true
});
}
}
openRequest.onsuccess = function(e) {
console.log('connection opened');
dataMethods.db = e.target.result
}
openRequest.onerror = function(e) {
console.log('could not open connection');
}
return dataMethods;
}
else {
console.log('indexedDB not supported');
}
}]);
and then here is my controller code:
stocks.controller('portfolio', ['$scope', '$http', 'stockData', 'database', function portfolio($scope, $http, stockData, database) {
$scope.getAllStocks = function(){
console.log('running getll stocks');
database.readOnlyStock().then(function(result) {
console.log('done');
$scope.allstuff = result;
}, function(){
console.log('no');
});
}
}]);
not sure where the issue is.
You need to return the promise from the readOnluStock method, not from the success handler within it
readOnlyStock: function () {
var deferred = $q.defer();
var transaction = this.db.transaction(["portfolio"], "readonly");
var store = transaction.objectStore("portfolio");
// var cursorRequest = store.openCursor();
var arrayOfStocks = [];
var keyRange = IDBKeyRange.lowerBound(0);
var cursorRequest = store.openCursor(keyRange);
cursorRequest.onsuccess = function (e) {
var cursor = e.target.result;
if (cursor) {
arrayOfStocks.push(cursor.value);
cursor.
continue ();
} else {
console.log(arrayOfStocks);
console.log('done!');
deferred.resolve(arrayOfStocks);
//return arrayOfStocks;
}
}
cursorRequest.onerror = function () {
console.log('could not fetch data');
}
// the return should be here
return deferred.promise;
},
if(cursor){
arrayOfStocks.push(cursor.value);
cursor.continue();
}
when the control reaches this part, it doesnt return anything explicitly. It means that by default undefined will be returned by javascript. So, you are trying to call then on undefined. You might want to change this to
if(cursor){
arrayOfStocks.push(cursor.value);
cursor.continue();
return deferred.promise;
}

Categories