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.
Related
I am working on Banking app using Angularjs on hackerrank where I am stuck at point.I am trying to call my function from Account summary controller but It is saying that No such function exist in my controller
Here is my code
AccountSummarycontroller.js
// Create the controller AccountSummaryController with getAccountSummary function that access accountSummaryService which is already defined in account-summary.service.js. Use $state for Transition from one page to another.
(function() {
'use strict';
var appContr = angular.module('abcbankApp.accountSummary', ['abcbankApp.accountSummaryService']);
appContr.controller('AccountSummaryController', function(AccountSummaryService) {
var ActSumCtrl = this;
// this.AccountSummaryService.getAccountSummary();
ActSumCtrl.accountList = [];
ActSumCtrl.accountList = AccountSummaryService.getAccountSummary();
});
})();
AccountSumaaryService.js
// Create the service AccountSummaryService with getAccountSummary function which should return the account details from data/account-summary.json using $http.get.
(function() {
'use strict';
var appServc = angular.module('abcbankApp.accountSummaryService', []);
appServc.factory('AccountSummaryService', ['$http',
function($http) {
return {
getAccountSummary: function() {
var data;
$http.get('./data/account-summary.json')
.then(function(response) {
data = response.data;
});
return data;
}
};
}]);
})();
But I am getting error something like below
abcbankApp.accsummary module AccountSummaryController should have a getAccountSummary function FAILED.Expected false to be true.
Testfile.js
describe('AccountSummaryController', function() {
it('Controller definition', function() {
expect(AccountSummaryController).toBeDefined();
});
it('should have a getAccountSummary function', function() {
expect(angular.isFunction(AccountSummaryController.getAccountSummary)).toBe(true);
});
});
});
})();
Any Help will be Appreciated.Thanks in advance
Well to answer your question lets begin with your test case as it is showing that AccountSummaryController.getAccountSummary should be defined
So you should call your function with your controller name and function name
Here is modified code
(function() {
'use strict';
var appContr = angular.module('abcbankApp.accountSummary', ['abcbankApp.accountSummaryService']);
appContr.controller('AccountSummaryController', function(AccountSummaryService) {
var ActSumCtrl = this;
// this.AccountSummaryService.getAccountSummary();
ActSumCtrl.accountList = [];
ActSumCtrl.getAccountSummary=function()
{
//move your logic here
}
ActSumCtrl.accountList = AccountSummaryService.getAccountSummary();
});
})();
Let me know in comment weather it worked or not
Its been like 6-7 months now with Angular and I have acquired not much but a relevant knowledge. But from past one day I am stuck on this error as shown below:
Error: [$injector:undef]
http://errors.angularjs.org/1.6.5/$injector/undef?p0=DataService
Stack trace: K/<#http://localhost:64965/Scripts/angular.min.js:7:76
Below is my service code:
(function () {
var DataService = function ($http) {
var allItems = function () {
return $http.get("./data/myData.json").then(function (serviceResp) {
return serviceResp.data;
});
};
};
app.factory("DataService", ["$http", DataService]);
}());
Below is my controller code:
(function () {
var ItemController = function ($scope, DataService) {
var allItems = function (data) {
$scope.collection = data;
};
};
app.controller("ItemController", ["$scope", "DataService", ItemController])
}());
The factory is returning the object but still I am getting the above error. I tried cleaning the cache several times and restarted the app many times.
The way you wrote your service is wrong.
It should be written in the following way:
var app = angular.module('ServiceExample',[]);
var serviceExampleController =
app.controller('ServiceExampleController', ServiceExampleController);
var serviceExample = app.service('NameOfTheService', NameOfTheService);
ServiceExampleController.$inject = ['NameOfTheService'] //protects from minification of js files
function ServiceExampleController(NameOfTheService){
serviceExampleController = this;
serviceExampleController.data = NameOfTheService.getSomeData();
}
function NameOfTheService(){
nameOfTheService = this;
nameOfTheService.data = "Some Data";
nameOfTheService.getSomeData = function(){
return nameOfTheService.data;
}
}
Then in the HTML you can use it like:
<div ng-controller = "ServiceExampleController as serviceExample">
{{serviceExample.data}}
</div>
If you are looking to use factory read this post.
Factory must return a value/obj. You are not returning anything. I have not tested, but it should work.
(function () {
var DataService = function ($http) {
var allItems = function () {
return $http.get("./data/myData.json").then(function (serviceResp) {
return serviceResp.data;
});
};
return allItems ;
};
app.factory("DataService", ["$http", DataService]);
}());
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/
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>
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.