Set and get value with factory, angularJS - javascript

Here's a service:
myApp.factory('myService', function() {
var test = 5;
return{
setTestVal: function(val){
test = val;
},
getTestVal: function(){
return test;
}
}
});
This is my controllers. One get the value and one sets it
function MyCtrl($scope, myService) {
$scope.test = myService.getTestVal();
}
function SetCtrl($scope, myService){
$scope.newTestVal = '';
$scope.setTestVal = function(val){
myService.setTestVal(val)
}
}
But the view is not updated when I set the value.
Fiddle: http://jsfiddle.net/HB7LU/7036/
Is this the wrong approach to setting and getting values?

No, this approach is perfectly fine, however the view has no idea when a new value is set, you have to setup a $watch on the shared property:
$scope.$watch(function() {
return myService.getTestVal();
}, function(value) {
$scope.test = value;
});
Fiddle: http://jsfiddle.net/HB7LU/7041/

You even could do it without $watch,
check fiddle i modified from yours :
var myApp = angular.module('myApp',[]);
myApp.factory('myService', function() {
var test = 5;
var obj = {
test : 5
}
return{
setTestVal: function(val){
test = val;
obj.test = val;
},
getTestVal: function(){
return test;
},
data : obj
}
});
function MyCtrl($scope, myService) {
$scope.test = myService.getTestVal();
$scope.data = myService.data;
}
function SetCtrl($scope, myService){
$scope.newTestVal = '';
$scope.setTestVal = function(val){
myService.setTestVal(val)
}
}
http://jsfiddle.net/no9rv2nb/

Related

Mock a function present in angularJS controller using jest - testing

I've a small controller written in angularJS.
1st function is actually calling a 2nd one to perform a calculation and return it back.
I want to mock the 2nd function in my testing, so that it returns value I've provided in mock instead of calling the function.
ABCService.js
var app = angular.module('mathModule', []);
app.controller('mathService', ['$scope', function($scope){
$scope.first = 0;
$scope.second = 0;
$scope.addTwoNumbers = function(x, y) {
return x + y;
};
$scope.callAddFunction = function() {
return $scope.addTwoNumbers(10, 20);
}
}]);
ABCServic.test.js
require('./mathService.js');
describe('Math service', function() {
beforeEach(
angular.mock.module('mathModule')
);
var $controller;
beforeEach(inject(function(_$controller_) {
$controller = _$controller_;
}));
describe('Test using 2 numbers', function() {
var $scope, controller;
beforeEach(function() {
$scope = {};
controller = $controller('mathService', { $scope: $scope });
});
it("Nested function", function() {
var total = $scope.callAddFunction();
expect(total).toEqual(31);
});
});
});
Here I want to mock addTwoNumbers() so that instead of calling we get value we've provided during testing.
Something like, Mock(addTwoNumbers(x,y)) = 0, so now callAddFunction will return 0 instead of 30, which it should be returning if not mocked.

Deletion of elements in array with $timeout does not work properly

The directive notification should delete "itself" after 5 seconds. However some elements get missed and some get deleted more than once. Identifier property is unique for each notification. Thanks for help.
Factory
angular.module('AdS').factory('notificationFactory', function () {
var notificationFactory = {};
notificationFactory.notifications = [];
notificationFactory.identifier =0;
notificationFactory.add = function(note){
if(typeof note!=='undefined'){
notificationFactory.identifier++;
note.identifier = notificationFactory.identifier;
notificationFactory.notifications.push(note);
}
}
notificationFactory.delete = function (note) {
if(typeof note!=='undefined'){
for(var i =0;i<notificationFactory.notifications.length;i++){
if(notificationFactory.notifications[i].identifier==note.identifier){
notificationFactory.notifications.splice(i,1);
}
}
}
return "";
}
notificationFactory.getNotifications = function () {
return notificationFactory.notifications;
}
return notificationFactory;
});
Directive
angular.module('AdS').directive('siteNotification', [
'$timeout',
function ($timeout) {
return {
restric: "E",
templateUrl: "/Templates/htmlBits/notification.html",
scope: {
note:"=",
center:"="
},
link: function (scope, element, attrs) {
$timeout(function () {
scope.center.delete(scope.note);
}, 5000);
scope.delete=function(note){
scope.center.delete(note);
}
}
};
}
]);
html
<site-notification ng-repeat="not in notificationCenter.notifications track by $index" center=notificationCenter note=not ></site-notification>
Rather than using an array for notificationFactory.notifications, you could use an object, with the unique identifier pointing to your note like so:
notificationFactory.notifications = {};
notificationFactory.add = function(note) {
if (typeof note!=='undefined') {
notificationFactory.identifier++;
notificationFactory.notifications[notificationFactory.identifier] = note;
}
}
notificationFactory.delete = function (note) {
if(typeof note!=='undefined'){
notificationFactory.notifications[notificationFactory.identifier] = null;
}
return "";
}
Also, in your directive, you seem to be injecting notificationFactory via the html. This is very unusual, the typical way to inject your factory is as follows:
angular.module('AdS').directive('siteNotification', [
'$timeout',
'notificationFactory',
function ($timeout, notificationFactory) {
...
}
The only reason I can see to do it differently way is if you want to be able to inject a different type of factory into your directive.

Javascript Getter/Setter in Angular Service with 2 Controllers

I am messing with javascript getters and setters in my service layer. I am using 2 controllers. The first controller just displays some text. The second controller allows hiding it. I am trying to figure out why one method works while another does not.
Here is the working example:
var app = angular.module('plunker', []);
app.factory('data', function () {
var fac = [];
var state = false;
fac.hideIt = function (hide) {
state = hide;
};
fac.hidden = function() {
return state;
}
return fac;
});
app.controller('MainCtrl', function($scope, data) {
$scope.name = 'World';
$scope.hide = data.hidden;
});
app.controller('SecCtrl', function($scope, data) {
$scope.hideAbove = function () {
var hide = true;
data.hideIt(hide);
};
});
Here is the not working example:
var app = angular.module('plunker', []);
app.factory('data', function () {
var fac = [];
fac.hide = {
state: false,
get get() {
return this.state
},
set set(hide) {
return this.state = hide;
}
};
return fac;
});
app.controller('MainCtrl', function($scope, data) {
$scope.name = 'World';
$scope.hide = data.hide.get;
});
app.controller('SecCtrl', function($scope, data) {
$scope.hideAbove = function () {
var hide = true;
data.hide.set = hide;
console.log(data.hide.get)
}
});
HTML (shared by both)
<body>
<div ng-controller="MainCtrl">
<div ng-hide="hide()">
<p>Hello {{name}}!</p>
</div>
</div>
<div ng-controller="SecCtrl">
<div ng-click="hideAbove()">CLICK HERE </div>
</div>
</body>
So my question is why does using the getter and setters in the non-working block of code not work?
You should use a service rather than a factory for this. A factory's value is set to the return value of the function you pass to it. There is not really a concept of "this" in a factory ("this" probably points to the window object). "this" in a service points to the service instance.
app.service('data', function () {
this.state = false;
this.hide = {
get value() {
return this.state;
},
set value(hide) {
this.state = hide;
}
};
});
See in your html:
ng-hide="hide()"
The problem is simple you need to change hide property into method inside MainCtrl
$scope.hide = function() {
return data.hide.get;
}
DEMO
But you will say why the first example worked?
Because in the first example data.hidden does return function literal while the latter example just returns the value from getter.

angular watch object not in scope

I have a service in which values can change from outside Angular:
angularApp.service('WebSocketService', function() {
var serviceAlarms = [];
var iteration = 0;
this.renderMessages = function(alarms, socket) {
if (! angular.equals(serviceAlarms, alarms)) {
serviceAlarms = alarms;
iteration++;
}
};
this.getAlarms = function () {
return serviceAlarms;
};
this.iteration = function () {
return iteration;
};
this.socket = initSocketIO(this);
});
The initSocketIO function makes callbacks to this services renderMessages() function and serviceAlarms variable gets changed on a steady basis.
Now i am trying to watch for changes in this service like so:
controllers.controller('overviewController', ['$scope', 'WebSocketService', function ($scope, WebSocketService) {
$scope.$watch(
function () {
return WebSocketService.iteration();
},
function(newValue, oldValue) {
$scope.alarms = WebSocketService.getAlarms();
},
true
);
}]);
to no avail. The second function provided to $watch never gets executed except on controller initialization.
I have tried with and without true as third parameter.
You should use $rootScope.$watch (not $scope.$watch)
I ended up using the solution below since $watch didn't work as excpected.
I refactored the solution to use $rootScope in combination with:
angularApp.run(['$rootScope', function($rootScope){
$rootScope.socket = {};
$rootScope.socket.alarms = [];
$rootScope.socket.faults = [];
$rootScope.socket.renderErrors = function(faults, socket) {
var faultArray = [];
angular.forEach(faults, function(error) {
error.value ? faultArray.push(error) : null;
});
if (! angular.equals($rootScope.socket.faults, faultArray)) {
$rootScope.socket.faults = faultArray;
$rootScope.apply();
}
};
$rootScope.socket.renderMessages = function(alarms, socket) {
if (! angular.equals($rootScope.socket.alarms, alarms)) {
$rootScope.socket.alarms = alarms;
$rootScope.$apply();
}
};
$rootScope.socket.socket = initSocketIO($rootScope.socket);
}]);
Now i have my socket-updated-model in all scopes to use freely in controllers and views.
Controller example:
$scope.acknowledgeAlarm = function(alarm) {
$scope.socket.socket.emit('acknowledgeAlarm', {
hash:alarm.icon.hash,
id:alarm.id
});
};
View example:
<div ng-repeat="alarm in socket.alarms">
{{alarm.name}} {{alarm.icon.progress}}
</div>

Angular service not passing between controllers

I have two controllers on a parallel scope level I need to pass data between:
function TableRowCtrl($scope, $http, sharedProperties) {
console.log(sharedProperties.getProperty());
$scope.items = sharedProperties.getProperty();
}
and
function SideNavCtrl($scope, $http, sharedProperties) {
$scope.customers = undefined;
var temp = "cats";
$http.get('data/customers.json').success(function(data) {
$scope.customers = data;
temp = "dogs";
sharedProperties.setProperty(temp)
});
sharedProperties.setProperty(temp);
console.log(sharedProperties.getProperty());
}
I am trying to use a service to do this (via examples I have seen) :
angular.module('myApp', []).service('sharedProperties', function() {
var property = "Cats";
return {
getProperty: function() {
return property;
},
setProperty: function(value) {
property = value;
}
};
});
However - when I try and set the data in the SideNavCtrl http success function, it does not bubble out - the service still returns 'cats' as its value. From what I have read, services are supposed to be global, and setting data in them should be permanent (as is its purpose). What am I doing wrong, and how can I get data between these two controllers on the same scope?
The problem is your TableRowCtrl saves the result of a function in its scope variable. When the service itself changes, the value in the scope does not because at that point, it's a simple property. You can either expose your service directly in the scope or wrap $scope.items in a function instead:
function TableRowCtrl($scope, $http, sharedProperties) {
$scope.items = function() { return sharedProperties.getProperty(); };
}
// And in your view
{{ items() }}
Or
function TableRowCtrl($scope, $http, sharedProperties) {
$scope.shared = sharedProperties;
}
// And in your view
{{ shared.getProperties() }}
Edit: Simple plunkr here
Edit #2:
If the problem is a binding that isn't updated because of an asynchronous process, you can use $scope.$apply:
$http.get('data/customers.json').success(function(data) {
$scope.customers = data;
temp = "dogs";
sharedProperties.setProperty(temp)
if(!$scope.$$phase)
$scope.$apply();
});
Edit 3:
I've recreated your $http.get and updated the plunkr and it works. Based on what you are showing in your questions, it should work using function instead of regular properties.
#SimomBelanger already identified the problem. I suggest using objects rather than primitives, then you don't need to call functions in your view:
<div ng-controller="TableRowCtrl">items={{items.property}}</div>
<div ng-controller="SideNavCtrl">customers={{customers}}</div>
app.service('sharedProperties', function () {
var obj = {
property: "Cats"
};
return {
getObj: function () {
return obj;
},
setObjProperty: function (value) {
obj.property = value;
}
};
});
function SideNavCtrl($scope, $timeout, sharedProperties) {
$scope.customers = undefined;
var temp = "cats";
$timeout(function () {
$scope.customers = 'some data';
temp = "dogs";
sharedProperties.setObjProperty(temp);
}, 2000);
sharedProperties.setObjProperty(temp);
}
function TableRowCtrl($scope, $http, sharedProperties) {
$scope.items = sharedProperties.getObj();
}
fiddle
In the fiddle I use $timeout to simulate an $http response.
Because getObj() returns a (reference to an) object, updates to that object are automatically picked up by the view.

Categories

Resources