AngularJS controller not update model from service - javascript

I am new to Angular, but i do not understand why controller updates data from service only in some cases ...
http://plnkr.co/edit/pV8m11Bc1vItDSmQKECS?p=preview
object1 and 2 are correctly updated, but objet3 is not.
Seems that Service.get3() is just fired once.
I understand that Angular watch for object changes, fine for object1 and object2 (that is a reference to object1) by the way, it's great.
But it's not working with object3 ... ok it's not a reference...
The question is : how to build new objects in service ?
Thank you,
Philippe
var app = angular.module('App', []);
app.factory('Service', [
function() {
var object1 = ["TOTO"];
return {
get1: function() {
return object1;
},
get2: function() {
var object2 = object1;
return object2;
},
get3: function() {
var object3 = [];
/*
object1.forEach(function(entry) {
object3.push(entry);
console.log(entry);
});
*/
for (i = 0; i < object1.length; i++) {
object3.push(object1[i]);
}
return object3;
},
set: function(newValue) {
object1.push(newValue);
}
}
}
]);
app.controller('Controller', ['$rootScope', '$scope', 'Service',
function($rootScope, $scope, Service) {
$scope.object1 = Service.get1();
$scope.object2 = Service.get2();
$scope.object3 = Service.get3();
$scope.set = function() {
Service.set("TITI");
}
}
]);

As mentioned the third object is a different array. In order to have it notify your controller of changes you'll probably want to register a watch on it:
http://plnkr.co/edit/EdXVKqe5eaZgROFDG9U9?p=preview
$scope.$watch(Service.get3, function(val) {
$scope.object3 = Service.get3();
});

The first two are bound to object1. The third is bound to a different array altogether.

Related

Two controllers with common factory and ng-repeat refresh

I've problem with AngularJS. Ng-repeat dosn't want to refresh loop when I add new item to JSON from another instance of controller
In first controller I set JSON
(function(){
'use strict';
angular.module('mainApp').controller('ListController', function($scope, FavoriteListService) {
$scope.addToFavorites = function(speaker){
FavoriteListService.setFavorites(speaker);
};
})();
In secound controller I have to display ng-repeat
(function(){
'use strict';
angular.module('mainApp').controller('ShowController', function($scope, FavoriteListService) {
$scope.favoritesList = FavoriteListService.getFavorites();
})();
Factory
(function () {
'use strict';
angular.module('mainApp').factory('FavoriteListService', function () {
var obj = {};
obj.getFavorites = function () {
var favorites = localStorage.getItem('speaker-favorites');
if (favorites == null) {
favorites = {};
} else {
favorites = JSON.parse(favorites);
}
return favorites;
};
obj.setFavorites = function (speaker) {
var favorites = obj.getFavorites();
favorites[speaker.uid] = {firstname: speaker.firstname, name: speaker.name};
localStorage.setItem('speaker-favorites', JSON.stringify(favorites));
};
return obj;
});
})();
Template:
<ul>
<li ng-repeat="(key, fav) in favoritesList">
{{fav.firstname}} {{fav.name}}
</li>
</ul>
Everything is fine, when set & display is in one controller.
If I want to use 2 controllers (or 2 instance of 1 controller) ng-repeat show correctly all items after load page, but when I add new item it doesn't refresh loop and doesn't show new item.
Is any method to fix it?
You either need to change repeater to (and assign that FavoriteListService to $scope variable):
ng-repeat="(key, fav) in FavoriteListService.getFavorites()"
or $watch that favorite list in your controller like that:
$scope.$watch(
function() { return FavoriteListService.getFavorites(); },
function(newValue, oldValue) {
if ( newValue !== oldValue ) {
$scope.favoritesList = newValue;
}
},
true
);
Because when you assign your service method return to scope method it's not being working like a reference.
Create an empty object, and use angular.copy to update the object:
app.factory('FavoriteListService', function () {
var obj = {};
//create empty object
var favorites = {};
obj.getFavorites = function () {
let update = localStorage.getItem('speaker-favorites');
if (update) {
//Use angular copy
angular.copy(update, favorites);
};
return favorites;
};
obj.setFavorites = function (speaker) {
favorites[speaker.uid] = {firstname: speaker.firstname, name: speaker.name};
localStorage.setItem('speaker-favorites', JSON.stringify(favorites));
};
return obj;
});
By using angular.copy, all of the controllers that use the getFavorites function get the same object reference and all of them see the same changes to its contents.
For more information, see AngularJS angular.copy API Reference

Dynamically set data on Angular-chart.js

To be honest I am a bit new to angularjs, so this may be problem with my fundamental understanding of angular, rather than angular-charts.
I have two controllers (PieItemsCtrl and PieCtrl) and I would like to communicate between them by using a factory service (called pieItems)
On the one hand the pieItems works as designed in the PieItemsCtrl.
ie:
$scope.slices = pieItems.list();
Whenever something changes in the pieItems service (ie another element is added), then the HTML is automatically updated via a repeater :
<div ng-repeat="(key, val) in slices">
However in the PieCtrl I have this line, and i would expect the pie chart to update automatically :
$scope.labels = pieItems.labelsItems();
$scope.data = pieItems.data();
It seems to set these data values upon loading/initialisation of the PieCtrl and that's it. Whenever the pieItems data changes these scope values are not updated.
The source of the two controller and factory object are below. And I also made an unworkable fiddle, incase that helps
PieItemsCtrl :
app.controller('PieItemsCtrl', function($scope, $http, $rootScope, pieItems) {
$scope.slices = pieItems.list();
$scope.buttonClick = function($event) {
pieItems.add(
{
Name: $scope.newSliceName,
Percent: $scope.newSlicePercent,
Color: $scope.newSliceColor
}
)
}
$scope.deleteClick = function(item, $event) {
pieItems.delete(item);
}
}
)
PieCtrl :
app.controller("PieCtrl", function ($scope, $timeout, pieItems) {
$scope.labels = pieItems.labelsItems();
$scope.data = pieItems.data();
});
pieItems :
app.factory('pieItems', function() {
var items = [];
var itemsService = {};
itemsService.add = function(item) {
items.push(item);
};
itemsService.delete = function(item) {
for (i = 0; i < items.length; i++) {
if (items[i].Name === item.Name) {
items.splice(i, 1);
}
}
};
itemsService.list = function() {
return items;
};
itemsService.labelsItems = function() {
var a = ['x', 'y'];
for (i = 0; i < items.length; i++) {
a.push(items[i].Name);
}
return a;
};
itemsService.data = function() {
var a = [50,50];
for (i = 0; i < items.length; i++) {
a.push(items[i].Percent);
}
return a;
};
return itemsService;
});
The controller doesn't notice when the value in your factory changes. To include your item-Array in an Angular digest-cycle, tell Angular to $watch that Array.
If you don't want to expose the Array, create a getter:
itemsService.get = function() { return items; }
Then you can include that getter in your $watch expression in your controller:
$scope.$watch(getItems, watcherFunction, true);
function getItems() {
return pieItems.get();
}
The getItems-Function gets called on digest cycle and fires the watcherFunction if the value changed and has the newData as argument. true as 3rd argument creates a deep watch.
function watcherFunction(newData) {
console.log(newData);
// do sth if array was changed
}
For more complex objets, you can use a $watchCollection.

Passing an array from service to controller

I cannot seem to figure out how to pass an array from service to a controller.
I have a simple service
.service('test', function() {
var array = []
return array;
})
And a controller where I call this function when a button is pressed
$scope.testArray = function() {
$scope.test = test.array;
console.log("test: ", $scope.test);
};
I get an error test is undefined. Can anyone explain to me please why this doesn't work and how to fix it? I tried storing that array in a separate object but no luck either. THanks
(See also: this SO question about Angular providers)
A service should put properties directly on this. So instead of
.service('test', function() {
var array = [];
return array;
})
try
.service('test', function() {
this.array = [];
})
(code style notwithstanding; many would suggest preferring function access over direct object access)
.service('test', function() {
var array = [];
this.getArray = function(){
return array;
};
})
Just change test.arraywith test:
JSFiddle
.controller('youCtrl', ['$scope', 'test', function ($scope, test) {
$scope.testArray = function() {
$scope.test = test;
console.log("test: ", $scope.test);
};
});
Add the array variable to your service.
angular.module('moduleName').service('test', function() {
this.array = [];
});
Inject your service into your controller.
angular.module('moduleName').controller('controllerName', function(test) {
$scope.test = test.array;
console.log("test: ", $scope.test);
});

How to get listeners for a variable in AngularJS?

In AngularJS I know I can attach a listener to a variable like this:
$scope.$watch("variableName", listenerFunc);
I'm wondering whether it's possible to query a variable to find out which functions are already listening to it.
Specifically, I'd like to do something like the following
if( listenerIsNotAlreadyAssigned("variableName",listenerFunc) ){
$scope.$watch("variableName", listenerFunc); // assign it
}
Is there a way to implement the code above in Angular? If so, how?
Here's one method that might be considered hacky.. Demo here (click).
app.controller('myCtrl', function($scope) {
function checkWatchString(prop) {
var found = false;
angular.forEach($scope.$$watchers, function(item, i) {
if (item.exp === prop) { found = true; }
});
return found;
}
$scope.$watch('foo', function() {
});
console.log(checkWatchString('bar')); //false
console.log(checkWatchString('foo')); //true;
});
If you store a reference to the creation of a watch, you get a function reference that cancels the watch. You could take the obvious approach and track this manually and also be keeping the cancel function for each watch available.
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope) {
$scope.foo = '123';
var reg = {};
if (!reg.foo) {
reg.foo = $scope.$watch('foo', function() {
console.log('foo changed');
});
}
});
I made an demo using a service for this: http://jsbin.com/AbAwObE/4/edit
Watch service
app.factory('watchService', function() {
var watchService = function(prop, unset) {
if (!watchService[prop]) {
return false;
}
if (unset) {
watchService[prop]();
delete watchService[prop];
}
return true;
};
return watchService;
});
Add $watch
watchService[prop] = $scope.$watch(prop, function() {
++$scope.changeCount;
});
Check $watch
if (watchService(prop)) {
Remove $watch
watchService(prop, true);

How to use angularjs factory?

var myappWebApp = angular.module('myappWebApp', ['ui.bootstrap']);
//factory
myappWebApp.factory('wired', function () {
this.currOp = false;
return {
currOp1 : this.currOp
}
});
// controller
myappWebApp.controller('wiredCtrl',
function ($scope, $http, wired) {
//data
$scope.currOp = wired.currOp;//why is this undefined?
$scope.currOpInText = wired.currOpInText();
$scope.altOpInText = null;
$scope.boxA = null;
....
How should my scope.currOp always automatically have the same value as wired.currOp?
If you are copying just a bool, you can't. Booleans are copied by value, so you don't get reference-based updates.
You might consider using an object with a boolean:
myappWebApp.factory('wired', function () {
this.state = { curOp: false };
return {
state: this.state
}
});
Then, when you reference it in your scope, you can do this:
myappWebApp.controller('wiredCtrl',
function ($scope, $http, wired) {
$scope.opState = wired.state;
});
And now when curOp changes, the controller will have the change. You can watch for changes:
$scope.$watch("opState.curOp", function(newVal, oldVal) {
// Handle changes in curOp
});
Or you can bind to it:
CurOp: {{state.currOp}}
Note: You asked: "why is this undefined?" The answer is because your service is exposing currOp1 but you are referencing currOp

Categories