I'm new to this..
I have a controller
.controller('AudioCtrl', function($scope, Audio, AudioSockets) {
$scope.audioSockets = AudioSockets.list;
$scope.$watch(function() {return AudioSockets.list}, function () {
$scope.audioSockets = AudioSockets.list;
console.log(AudioSockets.list);
});
That references a service
.service('AudioSockets', function() {
// public API
this.list = sockets;
});
Where I'm trying to use a Javascript array that get's updated via external code
var sockets = [];
How do I get the value of what's in those sockets into my Controller?
Add third parameter boolean true to make deep watch
$scope.$watch(function(){
return AudioSockets.list
},
function(oldVal,newVal){
console.log(newVal);
},true);
Related
I have two controllers: Controller1 and Controller2
In Controller1's $scope, I have set up all my values I need. Using the data in $scope, I'm trying to run certain functions and pass the return values to Controller2.
I was thinking about making a factory to pass variable from Controller1 to Controller2. However, I realized all input values I need lives in Controller 1. I wonder whether factory can persist the data when it runs in Controller1 and return that data when it runs again in Controller2.
Thanks
Factory is a singleton so it can be used to share data among different controllers or directives. Take a look at the fiddler here. I have created a factory 'sharedContext' which can be used to share any key-value pair across controllers using different $scope.
Factory
myApp.factory("sharedContext", function() {
var context = [];
var addData = function(key, value) {
var data = {
key: key,
value: value
};
context.push(data);
}
var getData = function(key) {
var data = _.find(context, {
key: key
});
return data;
}
return {
addData: addData,
getData: getData
}
});
From the controller that needs to share the object can call the 'addData' method of the factory to store the data under a key. The other controllers/directives which are interested in accessing the shared data can do so by calling the 'getData' method and passing the correct key.
Controller (Sharing Data)
function MyCtrl_1($scope, sharedContext) {
$scope.input_1 = 5;
$scope.input_2 = 15;
$scope.add = function() {
$scope.result = $scope.input_1 + $scope.input_2;
sharedContext.addData("Result", $scope.result)
}
}
Controller (accessing shared data)
function MyCtrl_2($scope, sharedContext) {
$scope.getData = function() {
$scope.result = sharedContext.getData("Result").value;
}
}
The only assumption here is that both the controllers need to use the exact key to share the data. To streamline the process you can use a constant provider to share the keys. Also note that I have used underscore.js to look for the key in the shared context dictionary.
This is the simplest solution that you can come up with. As you can see the factory is a simple object and because of that construct it's passed by reference not by value that means in both controller dataFactory is the same
http://plnkr.co/edit/eB4g4SZyfcJrCQzqIieD?p=preview
var app = angular.module('plunker', []);
app.controller('ControllerOne', function (dataFactory) {
this.formFields = dataFactory
});
app.controller('ControllerTwo', function (dataFactory) {
this.formData = dataFactory
});
app.factory('dataFactory', function () {
return {};
})
edit
app.factory('dataFactory', function () {
var factory = {
method1: function (arg) {
console.log('method1: ', arg)
factory.method2('called from method1')
},
method2: function (arg) {
console.log('method2: ', arg)
}
}
return factory;
})
I want to pass some data from one controller to a second one via an onClick-Event. I tried to use a service between the two controllers but it seems that the controller who receives the data from the service doesn't recognize the onClick-Event of the first controller which leads to static/non changing data.
OnClick function (Controller 1)
$scope.select = function(index){
vm.currentActive = index;
sessionService.setState(index);
};
Exchange service
app.service('sessionService', function() {
var state = null;
var setState = function(changestate){
state = changestate;
};
var getState = function(){
return state;
};
return {
setState: function(changestate){
setState(changestate);
},
getState: function(){
return state;
}
};
});
Receiving Controller (Controller 2)
app.controller('ContentController', function ($scope, sessionService)
{
var vm = this;
vm.currentActive = sessionService.getState();
});
In the end I want that the state of Controller 2 changes whenever the OnClick-Event is triggered in controller 1. Is this way with the service the best or what do recommend to change the data in controller 2 after a click ?
One option for watching the state of a service is to use $scope.$watch with a function that returns the value to be watched for changes.
$scope.$watch(function(){ return sessionService.getState(); }, function(newValue, oldValue){
//Do something
});
If the value in the service is changed, the watch will pick up the change on the next digest cycle. With this method there's no need to have your service or other controller try and signal that the value has changed.
If your service's getter method does not depend on this, you can simplify the watcher by just passing the getter method as the watch function rather than using a wrapper function.
$scope.$watch(sessionService.getState, function(newValue, oldValue){
//Do something
});
You can add onChange event to service:
app.service('sessionService', function() {
var state = null;
var callbacks = [];
var setState = function(changestate) {
callbacks.forEach(function(callback) {
callback(state, changestate);
});
state = changestate;
};
var getState = function() {
return state;
};
return {
setState: function(changestate) {
setState(changestate);
},
getState: function() {
return state;
},
onChange: function(fn) {
if (typeof fn == 'function') {
callbacks.push(fn);
}
}
};
});
The reason your Receiving Controller is not getting the updated value is because the state property is copied into vm.state at the point of the directive definition object's initialization.
vm.currentActive = sessionService.getState();
Here, getState is only called once, so it won't matter if that state value is later updated...
One Solution
One option would be to call getState from the controller's view (which will get re-called (i.e. the value will be updated) with every digest cycle)...note this strategy has performance implications...
Another Solution
Another option is to leverage the trickle down effect of referenced objects (or as Miško explains in this Angular Best Practices video, "...if you don't have a dot, you're doing it wrong..."
You could utilize this strategy by using an object to store the state in your Exchange Service...
app.service('sessionService', function() {
var data = {};
var setState = function(changestate){
data.state = changestate;
};
var getState = function(){
return data.state;
};
return {
setState: setState,
data: data
};
});
Receiving Controller
app.controller('ContentController', function ($scope, sessionService) {
var vm = this;
vm.data = sessionService.data;
});
Then whenever data.state is updated in sessionService, vm.data.state will (by virtue of referenced data) contain the updated data as well.
In other words, vm.data.state will always contain the most up to date value of sessionService.data.state because they both refer to the same object.
I want to be able to share data between two controllers so that I can send a boolean to the service from the first controller which is turn triggers a change in the second controller.
Here is what the service looks like
exports.service = function(){
// sets Accordion variable to false ;
var property = true;
return {
getProperty: function () {
return property;
},
setProperty: function(value) {
property = value;
}
};
};
Now the first controller
exports.controller = function($scope, CarDetailsService, AccordionService ) {
$scope.saveDetails = function() {
AccordionService.setProperty(false);
}
}
and the second one
exports.controller = function($scope, AccordionService ) {
$scope.isCollapsed = AccordionService.getProperty();
}
The use case is that when i click on a button on the first controller,the service updates the data inside it, which is then served on the second controller, thus triggering a change in the second controller.
I have been looking around for quite some time but couldn't find a solution to this. Maybe im just stupid.
On the second controller you can $watch the variable you change in the first:
scope.$watch('variable', function(newValue, oldValue) {
//React to the change
});
Alternatively, you can use the $broadcast on the rootScope:
On the first controller:
$rootScope.$broadcast("NEW_EVENT", data);
On the other controller:
scope.$on("NEW_EVENT", function(event, data){
//use the data
});
I'm new to Angular and I'm trying to create an app that pulls data from a dynamically generated JSON file every 5 minutes and updates the view with the new data. The JSON file contains all the data for the website, which is data for a slider and for a list of events. I've read that global data can be stored in the $rootScope, or retrieved and served using a .service or .factory. I've tried different ways with no success and I'm lost. What is the best way to periodically pull data from an api for the entire app, store it, and use it?
Currently I have this code:
app.factory("Poller", function Poller($http, $timeout){
var data = {'slider' : [], 'activities' : []};
var getData = function(){
$http.get('http://example.com/json.php')
.then(function(r){
data = r.data;
});
$timeout(getData, 1000*60*5);
}
getData();
return{
data: data
}
});
app.controller('ListController', function(Poller){
this.activities = Poller.data.activities;
});
I think you're looking for something like this:
See plnkr
app.factory("Poller", function Poller($http, $interval){
var data = {
'newData': {
'one' : '',
'key' : ''
},
'fetchCount': 0
};
var getData = function() {
data.fetchCount += 1;
$http.get('http://echo.jsontest.com/key/value/one/two')
.then(function(response){
data.newData = response.data;
});
};
var loopGetData = function() {
$interval(getData, 5000);
};
loopGetData();
return{
data: data,
};
});
app.controller('ListController', function($scope, Poller){
$scope.activities = {'one' : '', 'key' : ''};
$scope.Initialize = function () {
$scope.$watch(function () { return Poller.data.newData; },
function(newValue, oldValue) {
if (newValue !== oldValue) {
$scope.activities = newValue;
}
});
};
$scope.Initialize();
});
app.controller('CountController', function($scope, Poller){
$scope.fetchCount = 0;
$scope.Initialize = function () {
$scope.$watch(function () { return Poller.data.fetchCount; },
function(newValue, oldValue) {
if (newValue !== oldValue) {
$scope.fetchCount = newValue;
}
});
};
$scope.Initialize();
});
So the problem here looks like you have defined the function getData() inside the Poller factory that makes the ajax request, but didn't bind that to a key inside the factory's returning object.
Try adding this,
return{
data: data,
getData: getData
}
And then remove the getData() you have right before the returning block.
Now I feel like depending on your app's setup, if you want to achieve long polling effects, you should attach getData() to a variable in some top-level controller that is always present so that you can invoke the getData() periodically using $timeout.
Speaking of $timeout is that you should extract the $timeout outside of the getData() and so that the function itself simply makes the ajax request. More modular, single responsibility design that way, which makes that factory function more usable in other areas of your app if you ever need.
If you want to update the information after you finish the ajax requests every five minutes, you should looking into $broadcast and $rootScope, you can basically tell your entire Angular app that some rootScope variable or service has changed, and they will update accordingly. You need some kind of callback set up after the ajax request inside whichever controller you are invoking the getData() function.
I am building a subscriber/observer pattern for displaying data in realtime for my angular app.
The observer is built with a factory injected into the angular controller and whose role is to fetch data and update it. The basic code structure can he found in this fiddle: http://jsfiddle.net/ctrager/67QR7/3/
var myApp = angular.module('myApp', [])
.factory('MyFactory', [function () {
var Collection = {};
Collection.isLoaded = 0;
Collection.data = [1, 2];
Collection.username = "corey and eric";
Collection.update = function () {
Collection.data.push(new Date())
}
Collection.replace = function () {
// If you do Collection.data = []
// here you are doing the same thing
// as the empty collection bug. I can't
// tell you EXACTLY why this confuses angular
// but I'm 99% sure it's the same phenomenon
Collection.data = [new Date()]
}
Collection.replace_fixed = function () {
// This works
Collection.data.length = 0
Collection.data.push(new Date())
}
return Collection;
}])
function MyCtrl($scope, MyFactory) {
$scope.name = 'Eric';
$scope.items = MyFactory.data;
$scope.replace = function(){
console.log("replace")
MyFactory.replace()
//$scope.items = MyFactor.data;
}
$scope.replace_fixed = function(){
console.log("replace_fixed")
MyFactory.replace_fixed()
//$scope.items = MyFactor.data;
}
$scope.update = function(){
console.log("update")
MyFactory.update()
}
}
The factory (MyFactory) contains a collection (Collection.data). Any push (/splice) to that collection is reflected in the scope, but if I replace the entire collection (Collection.replace()) the change is no longer reflected in $scope. Any idea why?
This works:
http://jsfiddle.net/67QR7/4/
changed the thing stored on scope to be the factory instead of data. then the html repeat to do items.data.
So it looks like this is because you replaced the reference inside collection, but that doesn't change where $scope.items was pointing to.
So you are creating a reference to MyFactory.data from $scope.items. Angular puts a $watch on $scope.items and looks for changes. When you call MyFactory.replace, you change MyFactory.data, but $scope.items remains the same. So as far as your watch is concerned, nothing has happened.
You can fix this by using replace_fixed, or watch for changes to MyFactory.data. http://jsfiddle.net/KtB93/
$scope.MyFactory = MyFactory;
$scope.$watch("MyFactory.data", function(newData) {
console.log('myFactory.data changed');
$scope.items = newData;
});
Or alternatively (probably better), you can use a function as the watch expression so you don't have to plop MyFactory on the scope (http://jsfiddle.net/XAW54/1/):
$scope.$watch(function() {
return MyFactory.data;
}, function(newData) {
$scope.items = newData;
});