Dynamically set data on Angular-chart.js - javascript

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.

Related

How to share a $scope object between multiple controllers

I am facing problem while sharing a $scope object between 2 controllers.
In controller 'IssueBookCtrl',I am updating the books object element value like this.
$scope.books[i].issued = true;
Then I am using $emit service to share the $scope object with controller 'BookListCtrl_Librarian'.
$scope.$emit('update_parent_controller', $scope.books);
But when I run the view which is using the controller 'BookListCtrl_Librarian',i don't see the updated object.
controller.js
Controllers.controller('BookListCtrl_Librarian', ['$scope','$http','$location','$rootScope','BookData',
function ($scope, $http ,$location, $rootScope , BookData) {
$http.get('data/books.json').success(function(data) {
if($rootScope.books==='undefined'){
$rootScope.books = BookData.getData();
}
$scope.books = data;
});
$scope.options = ['bookId', 'cost'];
$scope.$on("update_parent_controller", function(event, books){
$scope.books = books;
});
$scope.issue = function(bookId) {
for (var i = 0, len = $scope.books.length; i < len; i++) {
if ($scope.books[i].bookId == bookId) {
$rootScope.book = $scope.books[i];
break;
}
}
$location.path('/issue/'+bookId);
}
$scope.return = function (bookId) {
for (var i = 0, len = $scope.books.length; i < len; i++) {
if ($scope.books[i].bookId == bookId) {
$rootScope.book = $scope.books[i];
break;
}
}
$location.path('/return/'+bookId);
}
$scope.backToLogin = function() {
$location.path("/main");
}
}]);
Controllers.controller('IssueBookCtrl', ['$scope','$rootScope','$http','$routeParams','$location',
function ($scope,$rootScope, $http, $routeParams, $location) {
var Id=$routeParams.bookId;
$http.get('data/books.json').success(function(data) {
$scope.books = data;
});
$scope.issue = function(Id) {
alert("issued");
for (var i = 0, len = $scope.books.length; i < len; i++) {
if ($scope.books[i].bookId === Id) {
$scope.books[i].issued = true;
$scope.$emit('update_parent_controller', $scope.books);
$location.path('/home/librarian');
break;
}
}
}
}]);
Please guide me,any help is much appreciated.
Thanks
hi u have to use rootscope instead of scope emit
$rootScope.$emit('update_parent_controller', $scope.books);
and in other controler
$rootScope.$on('update_parent_controller', function(event, books){
$scope.books = books;
});
You could try forcing a digest after altering a model:
$scope.$apply();
But I would recommended that you build a Books service to hold those shared models and logic. You can learn about creating your own custom services here.
Or you could nest your controllers (put one inside the other) so that the inner controller can reference the outer controller's models by using $parent variable.
By using either you should not have any problems with object updating as AngularJs runs dirty-checks when scope variables are changed.

angularJS: Dynamic header sorting not working

I am trying to implement a solution to sort a table by clicking its headers, using AngularJS.
I found a good example after doing a Google search: https://scotch.io/tutorials/sort-and-filter-a-table-using-angular
I am able to see the up and down arrows, but the table does not sort when I click them.
I think the problem resides in how the JSON object is formatted in my situation. I have not been able to figure it out, and I am hoping that with the information that I am providing on this post, I can get some help to understand what I am doing incorrectly.
Here is a copy of the JavaScript:
(function (define, angular) {
'use strict';
define(function () {
var opportunityController = function ($scope, Metadata, Factory) {
var vm = this;
//set the default sort type
vm.sortType = 'Products';
//set the default sort order
vm.sortReverse = false;
Factory.Data(caller.sp, caller.filter).then(function (payload) {
var data = angular.fromJson(payload.data).Table;
ProcessData(data);
});
function ProcessData(data) {
if (angular.isDefined(data)) {
var counter = 0;
vm.products = [];
vm.productsSet = FindByAsObjectArray(function (x) {
return (x.TypeName == "Product");
}, data);
for (var index = 0, length = vm.productsSet.length; index < length; index++) {
vm.products[index] = {
data: vm.productsSet[index]
};
}
}
}
};
return ['$scope','Metadata','Factory',opportunityController];
});
})(define, angular);
I got it to work, final version: https://jsfiddle.net/itortu/nhhppf53/
Many thanks.
You are using controllerAs
ng-controller="Company:OpportunityController as opportunity"
therefor you have to reference sortType and sortReverse in your ng-click like so:
ng-click="opportunity.sortType = 'Products'; opportunity.sortReverse = !opportunity.sortReverse"

Enrich angularjs json data with additional view logic properties

When I load my json data from the server I need to have additional view only properties on json data. But thats not possible.
Then I thought about creating angularjs models from factories like:
'use strict';
angular.module('TGB').factory('TestViewModel', function () {
function TestViewModel(test) {
this.id = test.id;
this.schoolclassCode = test.schoolclassCode;
this.testType = test.type;
this.creationDate = test.date;
this.number = test.number;
this.isExpanded = false;
}
return (TestViewModel);
});
Where do you create these viewmodels in angular?
At the moment I have this method in my Controller , call it there and assign the result to $scope.testViewModels = toTestListViewModel(tests)
function toTestListViewModel(tests)
{
var testListViewModel = [];
for (var i = 0; i < tests.length; i++) {
var testViewModel = new TestViewModel(tests[i]);
testListViewModel.push(testViewModel);
}
return testListViewModel;
}
Controller is a proper place to create your 'view model' that can be accessed using $scope.
.controller('sampleCtrl', function ($scope, testViewModel) {
$scope.vM = testViewModel;
}

Why am I getting ngRepeat:dupes when sending data from an Array into a differently named Array?

Error: ngRepeat:dupes Duplicate Key in Repeater
http://plnkr.co/edit/hZtIXkPM7dhpf4P7rd6W?p=preview
I have an array which ng-repeats a list of tags on the page. Next I have an ng-click which sends the tag data into the scope of another controller whois job it is to display those selected tags in another list.
It's easier to see the code in action in the plnkr above, but the basics are:
the first tags Array is in the cnt controller
when you click on a tag, it gets stored in the TagDetailsFactory service
I then broadcast an event to the view controller to then call the getTagDetails function in TagDetailsFactory to retrieve the saved tags and store them into the viewTags array in the view controller.
This is where I'm getting the ngDupes error :(
However, the array in cnt is named $scope.tags = [];
and the array in view is $scope.viewTags = [];
// Code goes here
angular.module('app', [])
.directive('tagDetails', function() {
return {
restrict: "E",
link: function($scope, el, attrs) {
// console.debug($scope, attrs);
},
scope:{
tag:'=ngModel'
},
template: '<div ng-show="tag.showDetails">{{tag.details}}</div>'
};
})
.controller('cnt', ['$scope',
'$rootScope',
'TagDetailsFactory',
function($scope,
$rootScope,
TagDetailsFactory) {
$scope.tags = [];
for(var i = 0; i < 100; i++) {
$scope.tags.push(
{ name: 'Foo Bar ' + i, details: 'Details' + i }
);
}
$scope.showTagDetails = function(t) {
t.showDetails = true;
}
$scope.leaveTag = function(t) {
t.showDetails = false;
}
$scope.sendTag = function(t) {
TagDetailsFactory.saveTagDetails(t);
$rootScope.$broadcast('updateView');
}
}])
.factory('TagDetailsFactory', function() {
var savedTags = [];
var saveTagDetails = function(tag) {
savedTags.push(tag);
}
var getTagDetails = function() {
return savedTags;
}
return {
saveTagDetails : saveTagDetails,
getTagDetails : getTagDetails
};
})
.controller('view', ['$scope',
'$rootScope',
'TagDetailsFactory',
function($scope,
$rootScope,
TagDetailsFactory) {
$scope.viewTags = [];
$scope.$on('updateView', function() {
console.log('updateView');
var tags = TagDetailsFactory.getTagDetails();
console.log(tags);
$scope.viewTags.push(tags);
});
// $scope.showTagDetails = function(t) {
// t.showDetails = true;
// }
// $scope.leaveTag = function(t) {
// t.showDetails = false;
// }
}]);
ng-repeat does not allow duplicate items (otherwise how can it keep track of them, if you wanted to updated something, for example?).
"
In order to deal with this problem, you can add track by $index to your ng-repeat value:
ng-repeat="data in dataset track by $index"
You can have it track by other values in your data also, but it needs to be unique.
Your Plunker

AngularJS controller not update model from service

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.

Categories