Angular service not passing between controllers - javascript

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.

Related

Angular/Ionic: update view with ng-repeat binds to an array

i'm new in Ionic and Angular JS.
In trying to update a collection (array) in ng-repeat directive.
That's my code.
VIEW:
<div ng-repeat="e in testList">...</div>
CONTROLLER:
.controller('TestCtrl', function ($scope, $stateParams) {
var param1 = $stateParams.param1;
$scope.testList = TestFactory.getTest(param1);
});
FACTORY
.factory('TestFactory', ['Auth', function (Auth) {
auth = Auth.get();
listObj = {};
auth.on('custom-trigger', function(index, value){
addElement(index, value);
});
function addElement(index, value){
var obj = {}
obj.test=(value);
listObj[index].push(obj);
};
var TestFactory = {
getTest: function (index) {
if (typeof listObj[index] == 'undefined')
listObj[index] = [];
return listObj[index];
}
};
return TestFactory;
}])
The object testList is populated after 'custom-trigger' fire: it works fine because I can see the new value in the log.
This trigger is fired at the begin of application (many time and it works fine every time) and after server notifications (here is the problem!!).
The problem is that the view is not updated. I think is a data binding problem.
You likely need to notify your controller than addElement has been triggered. Try adding another $broadcast and listener:
factory
.factory('TestFactory', ['$rootScope', 'Auth', function ($rootScope, Auth) {
// ...
function addElement(index, value){
var obj = {}
obj.test=(value);
listObj[index].push(obj);
// notify the app that `addElement` has occurred
$rootScope.$broadcast('elementAdded');
};
// ...
}])
controller
.controller('TestCtrl', function ($scope, $stateParams) {
var param1 = $stateParams.param1;
$scope.testList = TestFactory.getTest(param1);
// listen for elementAdded event; re-get testList
$scope.$on('elementAdded', function(){
$scope.testList = TestFactory.getTest(param1);
});
});

angularjs undefined object in service

I want to share data from controller 1 to another controller
I get an undefined error in my 2nd controller. Is anyone able to tell me whats wrong with my code?
Service
app.service('var_transfer_service', function(){
var test_var;
return {
getVar: function () {
return test_var;
},
setVar: function( _test_var ) {
test_var = _test_var;
console.log(test_var);//show the object content in my console
}
}
})
Controller 1
app.controller('homeCtrl', function($scope,$http, $filter ,var_transfer_service){
$scope.search_item = function ($event,item){
console.log(item)
var latt = item.lat;
var lngt = item.lng;
var_transfer_service.setVar(item);
}
});
Controller 2
app.controller('MapCtrl',function($scope,var_transfer_service, $state, $cordovaGeolocation) {
var transferred_var = var_transfer_service.getVar();
console.log(transferred_var); //undefined object
});
It's undefined because it's not initialized:
var test_var;
You only set a value on the setVar function which gets called in the $scope.search_item function in the secound controller (that you never call).
What is your indented behaviour?
You used a service, but wrote a factory.
Angular services return the function instance, so put functions on the function scope, using "this".
Using service:
app.service('var_transfer_service', function(){
var test_var;
this.getVar = function () {
return test_var;
};
this.setVar = function( _test_var ) {
test_var = _test_var;
console.log(test_var);//show the object content in my console
}
}
Basically, angular service returns the function itself, but factory return the return value of the function - so, you wrote a factory.
So your code would work, if you will use app.factory:
app.factory('var_transfer_service', function(){
var test_var;
return {
getVar: function () {
return test_var;
},
setVar: function( _test_var ) {
test_var = _test_var;
console.log(test_var);//show the object content in my console
}
}
})

How can i pass variable values from one controller to another? [duplicate]

This question already has answers here:
AngularJS: How can I pass variables between controllers?
(16 answers)
Closed 7 years ago.
I have two different controller where i am trying to pass variable values to do some actions, I used $broadcast angularJS event but its not working. Is there any other solution to achieve this task ?
I understand there is already question asked regarding variables between controller but i wanted to know what are other possible soultions.
ctrl1.js
$scope.viewAssessmentFrmCycle = function(assessmentId) {
$scope.$broadcast('viewAssessment',assessmentId);
}
ctrl2.js
$scope.$on('viewAssessment',function(s,assessmentId){
console.log(assessmentId);
$location.path('/rcsa/editAssessmentFromCycle/'+assessmentId+);
});
Use a service.
angular.module('myApp', [])
.service('ShareThis', function () {
var value = 'myValue';
return {
getValue: function () {
return value;
},
setValue: function(newValue) {
value = newValue;
}
};
});
Then you can access this in each controller by either setting or getting...
Eg:
myApp.controller('Ctrl1', function($scope, ShareThis) {
$scope.value = ShareThis.getValue();
});
myApp.controller('Ctrl2', function ($scope, ShareThis) {
$scope.setVal = function(val) {
ShareThis.setValue(val);
}
}
You can create a factory to hold the data.
Factory:
angular.module('myModule').factory('commonData', function(){
var commonValue = {};
return {
getData : function(){ return commonValue },
setData : function(newData) { commonValue = newData }
}
});
Then inject this factory into your controller and use the set and get functions to manipulate the data.
Use factory to hold your data..
var app = angular.module('test', []);
// create a mediator factory which will persist the data
app.factory("MediatorFactory", function() {
return {
obj: {
value: ""
}
};
});
app.controller("FirstCtrl", ["MediatorFactory", function(mediator) {
this.variable1 = mediator.obj;
}]);
app.controller("SecondCtrl", ["MediatorFactory", function(mediator) {
this.variable2 = mediator.obj; // this.variable2 = this.variable1 in the controller1
}]);

AngularJS sharing async data between controllers

There's quite a few topics out there covering issues with sharing data between controllers, but I havn't found any good answers for my case.
I have one controller that fetches data asynchronous using promise. The controller then makes a copy of the data to work with within that scope. I then have a second controller which I want also want to work on the same copy of data that of the first controller so they both share it.
Here's some code simplified to serve as example:
.controller('firstController', function ($scope, someService){
var vm = this;
someService.getData().then(function(data) {
angular.copy(data, vm.data); //creates a copy and places it on scope
someService.setCurrentData(vm.data)
}
});
.controller('secondController', function ($scope, someService){
var vm = this;
vm.data = someService.getCurrentData(); //Triggers before the setter in firstController
});
.factory('someService', function(fetchService){
var _currentData = {};
var getData = function(){
return fetchService.fetchData().then(function(data) { return data; });
};
var getCurrentData = function(){
return _currentData;
}
var setCurrentData = function(data){
_currentData = data;
}
});
As the getData is async will the setCurrentData be triggered after the getCurrentData, so getCurrentData gives a different object and does not change to the correct one. I know you can solve this with broadcast and watch, but I'm trying to avoid using it if possible.
Refactor your factory to check if the _currentData variable has already been set - then you can simply use callbacks:
app.factory('someService', function(fetchService){
var _currentData = null;
var setCurrentData = function(data){
_currentData = data;
}
var getData = function(callback) {
if (_currentData == null) {
fetchService.fetchData().success(function(data) {
setCurrentData(data);
callback(data);
});
} else {
callback(_currentData);
}
};
/*
var getCurrentData = function(){
return _currentData;
}
*/
});
Now, calling your getData service will check if the data is already got and stored, if so, use that, else go get it!
someService.getData(function(data) {
console.log(data); //yay for persistence!
})
I would solve in this way:
.controller('firstController', function ($scope, $rootScope, someService){
var vm = this;
someService.getData().then(function(data) {
angular.copy(data, vm.data); //creates a copy and places it on scope
someService.setCurrentData(vm.data);
$rootScope.$broadcast('myData:updated');
}
});
.controller('secondController', function ($scope, $rootScope, someService){
var vm = this;
$rootScope.$on('myData:updated', function(event, data) {
vm.data = someService.getCurrentData();
});
});

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>

Categories

Resources